Setting up free HTTPS with Heroku SSL and Let’s Encrypt

I was tempted to just switch over to google compute engine where I can get a(maybe two) VM and set up SSL/TLS with seemingly hassle-free configuration given that the configuration of initialising a VM being very straight-forward (with vast images available). And I could even set one up hosting my Neo4j database. Hmmmmm. Cost is a problem, whilst it is not that much of a big deal on GCP but still, while you are developing hobby project I would prefer some more straight-forward methods with minimal cost (so that I can see the value and pay in the future)

  1. Cost of a SSL/TLS Certificate
    In fact you can easily buy a SSL/TLS with providers such as godaddy, digicert etc. The price differs by the different type of certs — an Extended validation (EV) Certificates on digicert would cost $470 (so you get the Green Box showing the company name, pretty awesome)
  2. Cost of installing the certificate to the server
    With Paas like Heroku, the flexibility of configuration is much less (which is also the greatness of these services). So you can get add-ons in order to allow protocols. It wasn’t long ago that you needed to pay $20 a month to get SSL-endpoint enabled. hmmmmmmmmmmmmmmm. :/ With Iaas, you can do whatever you want, as the cost was on getting a well-geared(suited) VM.

I don’t want to leave heroku (for now), but I must get the Layer on. So, here comes Heroku SSL (beta)! Yes, it is for free. (Only if you’re with a paid dyno). It sounds contradicting but yeh. Heroku offers a hobby plan with $7/month per dyno. Not bad! :D

Heroku SSL is still on beta, so use it with your own risk. I am experimenting on a hobby dyno so I am incredibly happy to try. To use it, there are three simple steps:

  1. Acquire an SSL certificate from your SSL provider
  2. Upload the certificate to Heroku
  3. Update your DNS settings to reference the new SSL endpoint

I generated the SSL certificate with Let’s Encrypt:

Let’s Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit. Let’s Encrypt is a service provided by the Internet Security Research Group (ISRG).
- https://letsencrypt.org/about/
The objective of Let’s Encrypt and the ACME protocol is to make it possible to set up an HTTPS server and have it automatically obtain a browser-trusted certificate, without any human intervention. This is accomplished by running a certificate management agent on the web server.
- https://letsencrypt.org/how-it-works/

To get a free certificate from Let’s Encrypt, you’ll need to install certbot, which an automatic client that fetches and deploys SSL/TLS certificates to webserver. In this case with heroku, certbot will only be used to generate the certificate as we have no access to ssh for our heroku ‘server’. To install certbot, simply by

brew install certbot

Once install, it is time to get the certificate

sudo certbot certonly --manual

So it begins. For my case, I am getting the certificate for www.sonders.co, there’ll be this prompt asking you to put in the domain name(s), just click ‘OK’ afterwards

The the terminal will give you this lot:

Make sure your web server displays the following content at
http://www.sonders.co/.well-known/acme-challenge/xxxxxxxxxxxx-yyyy.zzzzzzzzzzzzzzzzzzz before continuing:
xxxxxxxxxxxx-yyyy.zzzzzzzzzzzzzzzzzzz
If you don’t have HTTP server configured, you can run the following
command on the target server (as root):
mkdir -p /tmp/certbot/public_html/.well-known/acme-challenge
cd /tmp/certbot/public_html
printf “%s” Gm35kFLiXnNtKT9OAOG_KPZvqMmYYAZU6DN-QRoGclg.s2I4ZV9Ne2CNtczlqXV9uw1ZdB5OSypG_cIdiuT7BwI > .well-known/acme-challenge/Gm35kFLiXnNtKT9OAOG_KPZvqMmYYAZU6DN-QRoGclg
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
“import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer((‘’, 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()”
Press ENTER to continue

It is to make sure that the certificate is created for the domain name specified earlier. So certbot will try to access to this http://www.sonders.co/.well-known/acme-challenge/xxxxxxxxxxxx-yyyy.zzzzzzzzzzzzzzzzzzz and expect to read this string ‘xxxxxxxxxxxx-yyyy.zzzzzzzzzzzzzzzzzzz’ from the endpoint. To do so, on node server, just add something like

app.get('/.well-known/acme-challenge/:content', function(req, res) {
res.send('xxxxxxxxxxxx-yyyy.zzzzzzzzzzzzzzzzzzz')
})

Once done so (add/commit/deploy to heroku), go back to the terminal and press ENTER to continue (as it said so)

If successful, it will show you a congratulation message and tell you the location of the certificate. Yeh then it is time to upload the certificate to heroku!

As this SSL free service on Heroku is still on beta, you will need to enable the lab mode for the app through CLI.

heroku labs:enable http-sni -a your-app
heroku plugins:install heroku-certs

Then add the certificate by

heroku _certs:add /etc/letsencrypt/live/www.sonders.co/fullchain.pem /etc/letsencrypt/live/www.sonders.co/privkey.pem

Before this, you can access to https://your-app.herokuapp.com with no problem with the certificate comes with heroku, but accessing https://www.your-app.com (given this is your custom domain) will give you an error of ‘Your connection is not private’ as the certificate on https://your-app.herokuapp.com is with domain name *.herokuapp.com so it is not a match!

To map that, check the DNS target, it will probably show you something like this:

$ heroku domains
=== your-app Heroku Domain
your-app.herokuapp.com
=== your-app Custom Domains
Domain Name DNS Target
─────────────── ─────────────────────────────
www.your-app.com www.your-app.com.herokudns.com
your-app.com your-app.com.herokudns.com

For my situation, I have had forwarding domain to https://www.sonders.co so I simply changed the CNAME record with host www and targeting sonders.co.herokudns.com

This is the most exciting part. Check if the cert works!

curl -vI https://www.sonders.co

IT WORKS!!!!!!!!!!!

Browse to https://www.sonders.co and click on the green https to check out the certificate! It is no longer the certificate with *.herokuapp but the Let’s Encrypt one.

:D Yayyyyyy. Good Bye to ‘Your connection is not private’