Cloudflare and Heroku: Encrypting End-to-End HTTPS
The redirect loop:
$ curl -i https://my-cool-websiteHTTP/2 301…location: https://my-cool-website/…
Why? It turned out Cloudflare was forwarding the HTTPS request as an HTTP requests to Heroku. My Rails app noticed that the request was not HTTPS, and then issued a redirect to the HTTPS site, resulting in an infinite loop.
After doing some reading, I realised that this behaviour is controlled by Cloudflare’s TLS Custom SSL/TLS settings (under Websites > Website > TLS/SSL). The setting was set to Flexible, which issues your requests as HTTP.
Now this might be suitable for your use case, but I wanted Cloudflare to both handle TLS termination to the client but also make HTTPS requests to Heroku, making it end-to-end encrypted. I tried the Full (Strict) mode, and immediately got this “525 SSL handshake failed” error:
What happened? Even though I had SSL configured on Heroku through Heroku’s Automatic Certificate Management (which uses Let’s Encrypt under the hood), Cloudflare didn’t accept it.
I decided to issue an Origin Server certificate through Cloudflare and install it on Heroku. In Cloudflare, visit Websites > Website > TLS/SSL > Origin Server and generate a certificate.
In Heroku, open your app > Settings > SSL Certificates and add a manual certificate. Copy the public and private keys as prompted.
With that in place, Cloudflare trusts the certificate on the Heroku app and does end-to-end encryption with HTTPS both between Cloudflare and the user and between Cloudflare and the app.
One side-effect of this is that the Cloudflare certificate will not work for the public web. This is probably what you want – nobody should be accessing your Heroku app unless via Cloudflare.