Search code examples
javaspringspring-boottomcattomcat8

Spring Boot application with embedded Tomcat behind reverse proxy not redirecting to https when asking for /contextPath without trailing slash


This problem seems to have been asked multiple times but for this particular case I haven't found a proper solution.

The application is a Spring Boot 1.5.x that uses a embedded Tomcat server and runs on Openshift. The latter has a Router with an HTTPS route that terminates the TLS tunnel and forwards the traffic over HTTP to the application pod. In addition it inserts the X-Forwarded- headers (including the X-Forwarded-Proto header) so that the application redirects are composed using the https protocol.

I have configured server.use-forward-headers: true in the Spring Boot Application and tested it:

1) OK -> https://ocproute/myapp/ redirects 302 to my home page keeping the https protocol (the Tomcat RemoteIpValve took care of it).

2) FAIL -> https://ocproute/myapp (note there's no trailing slash) redirects 302 to http://ocproute/myapp/ As you can see it changed the protocol to http since the RemoteIpValve hasn't been invoked yet).

The logs show that Tomcat's Http11InputBuffer receives the request and at some point redirects it without taking into account the X-Forwarded-Proto header.

How can this be fixed?

2019-06-03T17:31:59.230 ( -  -  -  -  - ) o.a.t.u.n.NioEndpoint DEBUG - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@1511311d:org.apache.tomcat.util.net.NioChannel@5209052:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:64871]], Read direct from socket: [595]
2019-06-03T17:31:59.230 ( -  -  -  -  - ) o.a.c.h.Http11InputBuffer DEBUG - Received [GET /myapp HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
X-Forwarded-Proto: https
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,en-US;q=0.8,es;q=0.7,cy;q=0.6
Cookie: JSESSIONID=4693A1F63CD3E18058F98E129D11CE57

]
...

Solution

  • To make this work I had to disable the context root redirection customizing the Tomcat Context:

    @Configuration
    class TomcatConfiguration : EmbeddedServletContainerCustomizer {
    
        override fun customize(container: ConfigurableEmbeddedServletContainer) {
            val factory = container as TomcatEmbeddedServletContainerFactory
            factory.tomcatContextCustomizers = listOf(CustomCustomizer())
    
        }
    
        class CustomCustomizer : TomcatContextCustomizer {
            override fun customize(context: Context) {
                context.mapperContextRootRedirectEnabled = false
                context.addServletContainerInitializer(WsSci(), null)
            }
        }
    
    }