The usual way

Normally, forcing HTTPS with an .htaccess file is pretty straightforward, and not too difficult. You simply add the following to the top of your .htaccess file:

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

You have a RewriteCond that checks whether HTTPS is on or off, and after that you create a RewriteRule that redirects the user to the same host/URI, but with HTTPS instead of HTTP. The L flag prevents any other rule in the .htaccess file from being applied, and the R flag is a redirect (the 301 status code is for SEO optimization).

See this for documentation on rewrite module for Apache server.

Why is it not working on Heroku?

So when you try this on an application hosted on Heroku (I’m using a Cedar stack), this won’t work because there will be a infinite redirect loop.

redirect loop error

The reason for that is that our rewrite condition is always failing. This is because Heroku has a sweet load balancer between the client and the application. So the client connects with HTTP (or HTTPS) to the load balancer, not the application. The load balancer then connects to our application using TCP(as described here), causing the HTTPS variable to be set to off.

To resolve the issue, Heroku uses the header X-Forwarded-Proto to pass on the protocol that was used by the client.

X-Forwarded-Proto is a non-standard header which is commonly used by other services, like Amazon Web Services.

With that knowledge, all we have to do is rewrite the RewriteCond to the following:

RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Now it should work. Please note that this won’t work on every server unless you set this header. If you want to make sure it works everywhere (for example your dev server), simply add both rewrite conditions:

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
shadow-left