Require HTTPS with Spring Security behind a reverse proxy

Spring Boot makes it dead simple (at least with embedded Tomcat).

1. Add the following lines to your application.properties:

server.forward-headers-strategy=native
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto

2. Do the following trick with your HttpSecurity configuration.

// final HttpSecurity http = ...
// Probably it will be in your `WebSecurityConfigurerAdapter.configure()`

http.requiresChannel()
            .anyRequest().requiresSecure()

Source is Spring Boot reference guide

84.3 Enable HTTPS When Running behind a Proxy Server

Please also check the answer below for a specifics related to Spring Boot 2.2


Kind of a followup to NeilMcGuigan's answer that showed that the solution was servlet container side.

Tomcat is even better. There is a valve dedicated to masking the side effects of a reverse proxy. Extract from Tomcat documentation for Remote IP Valve:

Another feature of this valve is to replace the apparent scheme (http/https), server port and request.secure with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").

Example of the valve configuration :

<Valve className="org.apache.catalina.valves.RemoteIpValve"
    internalProxies="192\.168\.0\.10|192\.168\.0\.11"
    remoteIpHeader="x-forwarded-for" proxiesHeader="x-forwarded-by"
    protocolHeader="x-forwarded-proto" />

That way with no other configuration of the application itself, the call to Request.isSecure() will return true if the request contains a header field of X-Forwarded-Proto=https.

I had thought of two other possibilities, but definitively prefere that one :

  • use a filter active before Spring Security ChannelProcessingFilter to wrap the request with a HttpServletRequestWrapper overriding isSecure() to process a X-Forwarded-Proto header - need writing and testing the filter and the wrapper
  • use a Spring BeanPostProcessor to look for a ChannelProcessingFilter and manually inject a ChannelDecisionManager able to consider the X-Forwarded-Proto header - really too low level