0

I have a REST app written in Java using Jetty Embedded, running on a Heroku environment. I would like to force all traffic to go through HTTPS. There are some good answers on how to achieve this such as this one and this one. However, all answers require the TLS/SSL certificate to be read from a file. I would like to use Heroku's Automated Certificate Management (details here) to avoid having to renew certificates every year as they expire. Using ACM means that I do not have access to the certificate file.

Is there any way to configure Jetty to force HTTPS without reading the certificate from a file, or somehow to still be able to use Heroku ACM?

Carlos Silva
  • 281
  • 3
  • 10

1 Answers1

1

You'll can do this with a servlet filter that implement doFilter like this:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest)request;
    HttpServletResponse resp = (HttpServletResponse)response;

    String protocol = req.getHeader("X-Forwarded-Proto");

    if (protocol.equals("http")) {
        String url = "https://" + req.getServerName() + req.getContextPath() + req.getServletPath();
        if (req.getPathInfo() != null) {
            url += req.getPathInfo();
        }

        System.out.println("Forwarding request to: " + url);
        resp.sendRedirect(url);
    } else {
        System.out.println("Not forwarding protocol: " + protocol);
        chain.doFilter(request, response);
    }
}

Here's a complete example using Tomcat, but the principle is the same.

codefinger
  • 10,088
  • 7
  • 39
  • 51
  • Thanks @codefinger. When using Chrome, X-Forwarded-Proto is not included as a header, so this doesn't work for me. However, I see that HttpServletRequest.getScheme() seems to correctly identify whether I'm using HTTP or HTTPS, so I will try this approach and post the results. – Carlos Silva Jun 29 '17 at 16:45
  • Okay so after investigating it turns out that: HttpServletRequest.getScheme() is not reliable (it returns "http" even when using HTTPS on Heroku), and HttpServletRequest.isSecure() is also not reliable (returns false when using HTTPS on Heroku). The X-Forwarded-Proto header is not sent to servers running locally, but it is sent to servers running on Heroku. This answer is correct. – Carlos Silva Jul 02 '17 at 12:12
  • I believe the trouble w/ getScheme is because SSL termination happens at the Heroku router (thus the connection is HTTP at the Servlet). Best to check both? – codefinger Jul 03 '17 at 04:22