Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I force HTTPS for a Spring Boot app on Heroku?

I tried using FORCE_HTTPS=true and SECURITY_REQUIRE_SSL=true as config variables, but neither works. I know that the former is supported by Cloud Foundry, but I've confirmed with Heroku that they do not support it. The SECURITY_REQUIRE_SSL property is supported by Spring Boot, but maybe only for basic auth?

like image 421
Matt Raible Avatar asked Dec 04 '22 21:12

Matt Raible


1 Answers

I was able to fix this by creating an HttpEnforcer filter:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HttpsEnforcer implements Filter {

    private FilterConfig filterConfig;

    public static final String X_FORWARDED_PROTO = "x-forwarded-proto";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if (request.getHeader(X_FORWARDED_PROTO) != null) {
            if (request.getHeader(X_FORWARDED_PROTO).indexOf("https") != 0) {
                String pathInfo = (request.getPathInfo() != null) ? request.getPathInfo() : "";
                response.sendRedirect("https://" + request.getServerName() + pathInfo);
                return;
            }
        }

        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // nothing
    }
}

And registering it in an existing @Configuration class.

@Bean
public Filter httpsEnforcerFilter(){
    return new HttpsEnforcer();
}

This is different from the solution I posted in the comments above because of the null check for pathInfo. Without this, it still works, but the Location does show up with null at the end.

$ curl -i http://www.21-points.com
HTTP/1.1 302 Found
Server: Cowboy
Connection: keep-alive
Expires: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
X-Xss-Protection: 1; mode=block
Pragma: no-cache
Location: https://www.21-points.comnull
Date: Tue, 31 Oct 2017 14:33:26 GMT
X-Content-Type-Options: nosniff
Content-Length: 0
Via: 1.1 vegur
like image 120
Matt Raible Avatar answered Jan 04 '23 11:01

Matt Raible