• 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ヘッダーは絶対パスになる。HOSTX-Forwarded-HostX-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-redirectstrueにしているにも関わらず、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 propertiesserver.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 ContaineruseRelativeRedirects

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ヘッダーに設定されたことを確認できた。