- Spring Boot: 3.2.6
- Nginx: 1.25
Spring Bootで相対パスのリダイレクト
Spring Bootでリダイレクトする際にreturn "redirect:/path";
という書き方をしているとする。
※他にも以下などの書き方がある。
return ResponseEntity
.status(HttpStatus.FOUND)
.location(URI.create("/path"))
.build();
デフォルトでは、return "redirect:/path";
だとLocation
ヘッダーは絶対パスになる。HOST
やX-Forwarded-Host
、X-Forwarded-Proto
などのヘッダー値を元に、URLのスキームやホストを補完して絶対パスを生成する。
$ curl -v http://localhost:8080/original
> GET /original HTTP/1.1
略
>
< HTTP/1.1 302
< Location: http://localhost:8080/path
相対パスにしたい場合は、server.tomcat.use-relative-redirects
という設定をapplication.propertiesでtrue
にすれば良い。
$ curl -v http://localhost:8080/original
> GET /original HTTP/1.1
略
>
< HTTP/1.1 302
< Location: /path
Nginxをリバースプロキシにする
絶対パスになってしまう問題
Spring Bootの前にNginxを置いて動作確認すると、server.tomcat.use-relative-redirects
をtrue
にしているにも関わらず、Location
ヘッダーが絶対パスになってしまう問題にあたった。
原因調査のためtcpdump
で以下の通信を確認した。
- Nginx -> Spring Boot
- Spring Boot -> Nginx
- LB -> Nginx
tcpdumpのinstall
まずはtcpdumpをinstall。
# apt update && apt-get -y install tcpdump
実際の環境はKubernetes上で、tcpdump
はSpring Bootのコンテナ(Nginxコンテナがサイドカーになっている)に入って実行したが、特にKubernetes固有の事情はない。
Nginx -> Spring Boot, Spring Boot -> Nginx
行き帰りの通信を見るためdst
は指定しないで実行した。
# tcpdump -i any port 8080 -Ans 262144 | grep -A15 /original
Pl..Pl..GET /original HTTP/1.0
X-Forwarded-For: 略
略
以降、戻りの通信
Location: https://example.com/path
略
Spring Boot -> Nginxの時点でLocation
ヘッダーが絶対パスになっていることが分かった。Nginx自身が相対パスを絶対パスに変えてしまっているわけではなかった。Nginxの設定ではproxy_redirect off;
を入れているので想定通り。
Nginxが付与しているヘッダー等が原因で、Spring Bootが相対パスを返せなくなっている可能性が高いと考え、Nginx -> Spring Bootの通信とLB -> Nginxの通信を見比べた。
LB -> Nginx
Nginxのポートは8082のため、dst port 8082
でポート指定をした。
# tcpdump dst port 8082 -Ans 262144 | grep -A15 /original
Pl..Pl..GET /original HTTP/1.1
X-Forwarded-For: 略
略
ここでHTTPのプロトコルバージョンが違うことに気付いた。
HTTP/1.0とHTTP/1.1
ローカル上でSpring Bootにcurlする際、HTTP/1.0
を指定してみるとどうなるか試した。
$ curl -v http://localhost:8080/original --http1.0
> GET /original HTTP/1.0
略
>
< HTTP/1.1 302
< Location: http://localhost:8080/path
Spring Bootに直接curlしても絶対パスになってしまう問題が再現できたので、この問題の原因がHTTP/1.0
であることが特定できた。
server.tomcat.use-relative-redirects
について調べると、https://qiita.com/niwasawa/items/551899882eb78a473adfが引用している箇所にHTTP/1.1
以上を要求していることが書いてあった。
Common Application propertiesのserver.tomcat.use-relative-redirects
Whether HTTP 1.1 and later location headers generated by a call to sendRedirect will use relative or absolute redirects.
Apache Tomcat 9 Configuration Reference (9.0.37) - The Context ContainerのuseRelativeRedirects
Controls whether HTTP 1.1 and later location headers generated by a call to
javax.servlet.http.HttpServletResponse#sendRedirect(String)
will use relative or absolute redirects.
NginxのリバースプロキシでHTTP/1.1を使う
Module ngx_http_proxy_moduleによると、プロキシで使用されるデフォルトのHTTPプロトコルバージョンは1.0であることがわかった。Nginxの設定でproxy_http_version 1.1;
を追加し、テストしたところ、問題なく相対パスがLocation
ヘッダーに設定されたことを確認できた。