概要
- Spring Boot の server.tomcat.use-relative-redirects の設定によって、指定したリダイレクトが Location ヘッダにてどのような値でセットされるか検証する
検証環境
- Spring Boot 2.3.3
- Spring WebMVC 5.2.8
- Apache Tomcat Embed 9.0.37
- Java 14 (AdoptOpenJDK 14.0.2)
- macOS Catalina
server.tomcat.use-relative-redirects の設定
HttpServletResponse#sendRedirect の呼び出しによって生成された HTTP 1.1 以降の Location ヘッダーが相対リダイレクトを使用するか絶対リダイレクトを使用するかを制御する設定。application.properties などで設定できる。
server.tomcat.use-relative-redirects=false
true を指定すると、HttpServletResponse#sendRedirect に指定された相対リダイレクトはそのまま Location ヘッダに指定される。
false を指定すると、HttpServletResponse#sendRedirect に指定された相対リダイレクトは絶対リダイレクトに変換されて Location ヘッダに指定される。
server.tomcat.use-relative-redirects のデフォルト値は false になっている。
Whether HTTP 1.1 and later location headers generated by a call to sendRedirect will use relative or absolute redirects.
server.tomcat.use-relative-redirects は Apache Tomcat の useRelativeRedirects 設定と同様のもの。
Apache Tomcat 9 Configuration Reference (9.0.37) - The Context Container
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. Relative redirects are more efficient but may not work with reverse proxies that change the context path. It should be noted that it is not recommended to use a reverse proxy to change the context path because of the multiple issues it creates. Absolute redirects should work with reverse proxies that change the context path but may cause issues with the org.apache.catalina.filters.RemoteIpFilter if the filter is changing the scheme and/or port. If the org.apache.catalina.STRICT_SERVLET_COMPLIANCE system property is set to true, the default value of this attribute will be false, else the default value will be true.
検証するリダイレクトの指定方法
ビュー名に redirect: プレフィックスを使用
ビュー名に redirect: プレフィックスを使用することでリダイレクト先を指定することができる。
@GetMapping("/controller/relative")
public ModelAndView relative() {
// ビュー名に相対リダイレクトを指定
return new ModelAndView("redirect:/controller/hello");
}
server.tomcat.use-relative-redirects=true の場合はそのまま相対リダイレクトが Location ヘッダに指定される。
server.tomcat.use-relative-redirects=false の場合は相対リダイレクトが絶対リダイレクトに変換されて Location ヘッダに指定される。
Spring 内部で HttpServletResponse#sendRedirect を呼び出していると思われる。
Spring Web MVC サーブレットスタック - ドキュメント
ビュー名の特別な redirect: プレフィックスを使用すると、リダイレクトを実行できます。UrlBasedViewResolver (およびそのサブクラス)は、これをリダイレクトが必要な命令として認識します。ビュー名の残りはリダイレクト URL です。
最終的な効果は、コントローラーが RedirectView を返した場合と同じですが、コントローラー自体が論理ビュー名の観点から動作できるようになりました。論理ビュー名(redirect:/myapp/some/resource など)は現在のサーブレットコンテキストに関連してリダイレクトし、redirect:https://myhost.com/some/arbitrary/path などの名前は絶対 URL にリダイレクトします。
コントローラーメソッドに @ResponseStatus のアノテーションが付けられている場合、アノテーション値は RedirectView によって設定されたレスポンスステータスよりも優先されることに注意してください。
ResponseEntity.BodyBuilder#location
ResponseEntity.BodyBuilder の location メソッドでリダイレクト先を指定することができる。
@GetMapping("/restcontroller/relative")
public ResponseEntity<?> relative() {
// Location ヘッダの値に相対リダイレクトを指定
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create("/restcontroller/hello"))
.build();
}
server.tomcat.use-relative-redirects が true でも false でも、指定した相対リダイレクトはそのまま Location ヘッダに指定される。
Spring 内部で HttpServletResponse#sendRedirect を呼び出していないと思われる。
HttpServletResponse#sendRedirect
HttpServletResponse の sendRedirect メソッドでリダイレクト先を指定することができる。
@GetMapping("/restcontroller/sendRedirect/relative")
public void sendRedirectRelative(HttpServletResponse res) throws IOException {
// 相対リダイレクトを指定
res.sendRedirect("/restcontroller/sendRedirect/hello");
}
server.tomcat.use-relative-redirects=true の場合はそのまま相対リダイレクトが Location ヘッダに指定される。
server.tomcat.use-relative-redirects=false の場合は相対リダイレクトが絶対リダイレクトに変換されて Location ヘッダに指定される。
検証用のコントローラークラスを2つ用意
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class SampleController {
@GetMapping("/controller/relative")
public ModelAndView relative() {
// ビュー名に相対リダイレクトを指定
return new ModelAndView("redirect:/controller/hello");
}
@GetMapping("/controller/absolute")
public ModelAndView absolute() {
// ビュー名に絶対リダイレクトを指定
return new ModelAndView("redirect:http://localhost:8080/controller/hello");
}
}
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
@RestController
public class SampleRestController {
@GetMapping("/restcontroller/relative")
public ResponseEntity<?> relative() {
// Location ヘッダの値に相対リダイレクトを指定
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create("/restcontroller/hello"))
.build();
}
@GetMapping("/restcontroller/absolute")
public ResponseEntity<?> absolute() {
// Location ヘッダの値に絶対リダイレクトを指定
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create("http://localhost:8080/restcontroller/hello"))
.build();
}
@GetMapping("/restcontroller/sendRedirect/relative")
public void sendRedirectRelative(HttpServletResponse res) throws IOException {
// 相対リダイレクトを指定
res.sendRedirect("/restcontroller/sendRedirect/hello");
}
@GetMapping("/restcontroller/sendRedirect/absolute")
public void sendRedirectAbsolute(HttpServletResponse res) throws IOException {
// 絶対リダイレクトを指定
res.sendRedirect("http://localhost:8080/restcontroller/sendRedirect/hello");
}
}
Location ヘッダの値を検証する
server.tomcat.use-relative-redirects=true の場合
ModelAndView に指定した相対リダイレクト /controller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/controller/relative
HTTP/1.1 302
Location: /controller/hello
Content-Language: ja-JP
Content-Length: 0
Date: Tue, 25 Aug 2020 11:10:27 GMT
ModelAndView に指定した絶対リダイレクト http://localhost:8080/controller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/controller/absolute
HTTP/1.1 302
Location: http://localhost:8080/controller/hello
Content-Language: ja-JP
Content-Length: 0
Date: Tue, 25 Aug 2020 11:10:31 GMT
ResponseEntity に指定した相対リダイレクト /restcontroller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/relative
HTTP/1.1 302
Location: /restcontroller/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:10:35 GMT
ResponseEntity に指定した絶対リダイレクト http://localhost:8080/restcontroller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/absolute
HTTP/1.1 302
Location: http://localhost:8080/restcontroller/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:10:38 GMT
HttpServletResponse#sendRedirect に指定した相対リダイレクト /restcontroller/sendRedirect/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/sendRedirect/relative
HTTP/1.1 302
Location: /restcontroller/sendRedirect/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:10:41 GMT
HttpServletResponse#sendRedirect に指定した絶対リダイレクト http://localhost:8080/restcontroller/sendRedirect/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/sendRedirect/absolute
HTTP/1.1 302
Location: http://localhost:8080/restcontroller/sendRedirect/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:10:45 GMT
server.tomcat.use-relative-redirects=false の場合
ModelAndView に指定した相対リダイレクト /controller/hello が絶対リダイレクト http://localhost:8080/controller/hello に変換されて Location ヘッダに指定される。
$ curl --include http://localhost:8080/controller/relative
HTTP/1.1 302
Location: http://localhost:8080/controller/hello
Content-Language: ja-JP
Content-Length: 0
Date: Tue, 25 Aug 2020 11:09:21 GMT
ModelAndView に指定した絶対リダイレクト http://localhost:8080/controller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/controller/absolute
HTTP/1.1 302
Location: http://localhost:8080/controller/hello
Content-Language: ja-JP
Content-Length: 0
Date: Tue, 25 Aug 2020 11:09:25 GMT
ResponseEntity に指定した相対リダイレクト /restcontroller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/relative
HTTP/1.1 302
Location: /restcontroller/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:09:29 GMT
ResponseEntity に指定した絶対リダイレクト http://localhost:8080/restcontroller/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/absolute
HTTP/1.1 302
Location: http://localhost:8080/restcontroller/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:09:35 GMT
HttpServletResponse#sendRedirect に指定した相対リダイレクト /restcontroller/sendRedirect/hello が絶対リダイレクト http://localhost:8080/restcontroller/sendRedirect/hello に変換されて Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/sendRedirect/relative
HTTP/1.1 302
Location: http://localhost:8080/restcontroller/sendRedirect/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:09:42 GMT
HttpServletResponse#sendRedirect に指定した絶対リダイレクト http://localhost:8080/restcontroller/sendRedirect/hello がそのまま Location ヘッダに指定される。
$ curl --include http://localhost:8080/restcontroller/sendRedirect/absolute
HTTP/1.1 302
Location: http://localhost:8080/restcontroller/sendRedirect/hello
Content-Length: 0
Date: Tue, 25 Aug 2020 11:09:46 GMT
参考資料
- ModelAndView (Spring Framework 5.2.8.RELEASE API) - Javadoc 日本語訳
- ResponseEntity (Spring Framework 5.2.8.RELEASE API) - Javadoc 日本語訳
- HttpServletResponse (Servlet 4.0 API Documentation - Apache Tomcat 9.0.37)
- Spring Boot アプリケーションプロパティ一覧 - ドキュメント
- Spring Boot Reference Documentation - Common Application properties
- URL とは何か - ウェブ開発を学ぶ | MDN
- Location - HTTP | MDN