HTTP to HTTPS redirection

You’ve encountered it before probably. You want to securely publish a webapplication. The webapplication works with the http protocol and thus hosted on port 80 (typically) on your webserver. One of the requirements when publishing externally is to this securely which means encrypting the http traffic with TLS (https).

You go ahead and use your reverse proxy to open up an externally available https port (typically 443), install a certificate and point the traffic to your webserver on port 80. This means that the client hits the reverse proxy on port 443 (so this traffic is encrypted), but internally (from the reverse proxy to the webserver) the traffic is unencrypted. So far, so good.

However, when you test the webapplication you get the dreaded “page cannot be displayed” (or something of sorts). You start troubleshooting. You can ping and you can successfully access port 443. You start monitoring your webrequest with a tool (e.g. Fiddler, Wireshark,…) and notice that your request does get a response from the webserver, but the subsequent request fails. Then you see the problem, that subsequent request is not hitting port 443, but port 80! Then you look at the response of the webserver. And there’s the culprit, you see a 302 redirect to http://…

This means that the webserver is successfully receiving the request, and sends a response back, however the response does not consider that the client needs to hit the reverse proxy on port 443. Indeed, the application is unaware of this (it gets http requests from the reverse proxy) and just sends http responses (with http:// urls in the body of the response) back.

Here I’m going to present several solutions for this problem.

Solution 1

The most preferred solution is to make the web application aware that it’s being fronted by a reverse proxy. Some applications are aware of these kind of configurations and can be configured for this (SharePoint is a prime example). Other applications just need a hint. This involves adding a http header (X-Forwarded-Proto: https) in the request on the reverse proxy. This particular header helps the webapplication to identify the protocol (https in this case) that a client used to connect. Knowing this, the webapplication then serves https urls in the body of the response, while still sending the response with http.

Advantages

  • Easy to implement
  • No or minor changes to the reverse proxy
  • No or minor changes to the webapplication

Disadvantages

  • The webapplication must support this

Solution 2

You could listen also on port 80 (http) on your reverse proxy and redirect those requests to 443 (https)

Advantages

  • No changes required on the webapplication
  • Minor change on the reverse proxy

Disadvantages

  • Introduces a very small delay on the client (1 extra response + request) per hit on port 80, could become noticeable if page contains alot of http:// links
  • Some types of content (e.g. HTML5 streaming video, streaming in general) will not like this
  • Less secure. Redirecting http to https is prone to Man in the middle (MITM) attacks (thanks to Jan Tytgat for this suggestion)

Solution 3

Rewrite the body of the web page on the response to modify http:// urls to https://

Advantages

  • No changes required on the webapplication

Disadvantages

  • Introduces a slight delay on the reverse proxy (when modifying the body), normally only noticeable with millions of hits per second though
  • Difficult to configure. You need to configure the policy to only change links to your webapplication. If you change ALL urls with mixed content (for example images on another webserver), you would also change the urls to external content which might not be accessible on https. Even more difficult when client-side javascript is being used where urls are being generated in code.

Solution 4

Forego http completely, and configure https on the webapplication.

Advantages

  • More secure
  • Minor change on the reverse proxy (address the webserver on port 443 instead of 80)

Disadvantages

  • You need to configure a certificate on the webserver too (two places to address when updating a certificate)
  • Needs support from the webapplication (will need to be configured for https)
  • Webserver might already have another application configured on port 443. This can be resolved by using host names or using another port (e.g. 444), however the latter will introduce accessibility issues when the application is also accessed internally (the user will need to explicitly add the port to the url)
  • Additional performance hit on webserver (must now also process encryption)

So there you have it, 4 possible solutions, with the first one preferred, and 3 others if your webapp doesn’t have out-of-the-box support.