Use Let’s Encrypt TLS certificate on Heroku

Prem Sichanugrist
Sikachu's Blog
Published in
5 min readDec 11, 2015

--

Let’s Encrypt is a new certificate authority that can grant a TLS certificate for your domain name. Unlike most of other certificate authorities, it grants you a certificate via an automated script, and the certificates are being given out for free. This reduces the cost of setting up a secure website for your users.

If you deploy your application on Heroku, you will find out that it’s not a straightforward process to obtain a Let’s Encrypt certificate for your site because of Heroku’s platform architecture.

Please note: To use Let’s Encrypt TLS certificate on Heroku, you will need to either enable SSL Endpoint add-on for your app, which will cost you $20/month. Alternatively, you can use Heroku SSL (Beta) add-on, which is free with paid dyno (starting from $7/month with Hobby plan). If you would like to have a free TLS certificate for your domain on free dynos, you can consider using Universal SSL certificate from CloudFlare instead.

To start, download and install Let’s Encrypt ACME (Automatic Certificate Management Environment) client.

On most platforms, you can clone the project and run letsencrypt-auto --help to install Let’s Encrypt client and its dependency:

$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt
$ ./letsencrypt-auto --help

On OS X, Let’s Encrypt script will download and install Python using Homebrew. Since I find that was overkill, I actually just use Python and pip that shipped with OS X instead:

$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt
$ sudo easy_install pip
$ sudo pip install virtualenv
$ ./letsencrypt-auto --help

If the script outputs a help message, that means you machine is ready. Now, you can go ahead and generate a certificate for your domain:

$ ./letsencrypt-auto certonly --manual --email youremail@yourdomain.com -d yourdomain.com -d www.yourdomain.com

You should be getting this output:

Updating letsencrypt and virtual environment dependencies.......
Running with virtualenv: sudo /Users/sikachu/.local/share/letsencrypt/bin/letsencrypt certonly --manual --email youremail@yourdomain.com -d yourdomain.com -d www.yourdomain.com
Make sure your web server displays the following content at
http://yourdomain.com/.well-known/acme-challenge/SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo before continuing:
SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo.A8ScxIDy_TDWez5RKFU8jiL1CzZTfN6jP2fsDN7LWnYIf you don’t have HTTP server configured, you can run the following
command on the target server (as root):
mkdir -p /tmp/letsencrypt/public_html/.well-known/acme-challenge
cd /tmp/letsencrypt/public_html
printf “%s” SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo.A8ScxIDy_TDWez5RKFU8jiL1CzZTfN6jP2fsDN7LWnY > .well-known/acme-challenge/SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo
# 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

I’ve highlighted the part that you should pay attention to, which is putting up that ACME challenge file on your site.

If your application is a Rails application, you can just create an ACME challenge file in its public folder. Open a new terminal and navigate to your site’s repository, then create the necessary file:

$ cd ~/Work/yoursite
$ mkdir -p public/.well-known/acme-challenge
$ printf "%s" SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo.A8ScxIDy_TDWez5RKFU8jiL1CzZTfN6jP2fsDN7LWnY > public/.well-known/acme-challenge/SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo

(Note: Don’t forget to change the content and filename of ACME challenge to the one that was given to you.)

Commit the change, push to Heroku, then try to access the challenge file:

$ git add public/.well-known
$ git commit -m "Add Let's Encrypt ACME challenge"
$ git push heroku master
# After the push is done, try cURL it.
$ curl http://yourdomain.com/.well-known/acme-challenge/SU9p8-K8mqqHzRPvh3ocdcloLTaYkJftr_LcU5NOHAo

If you get the same challenge back, you are good to go. Now, go back to your previous Terminal and press ENTER. You will get another instruction for verifying www.yourdomain.com. Repeat the steps to adding the challenge file from above.

If you place those files correctly, you will get this message:

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/yourdomain.com/fullchain.pem. Your cert
will expire on 2016-03-10. To obtain a new version of the
certificate in the future, simply run Let’s Encrypt again.
- If like Let’s Encrypt, please consider supporting our work by:
Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

(Note: If you get a failure message instead, make sure that you disable any redirection script that changes the domain name such as redirecting yourdomain.com to www.yourdomain.com.)

Your certificate and its private key is now saved locally on your machine at /etc/letsencrypt/live/yourdomain.com. Now you just need to add that certificate to your Heroku app using SSL Endpoint or Heroku SSL (Beta).

Use SSL Endpoint add-on

This is the usual way of adding a TLS certificate to Heroku application. This method will cost you $20/month, but it is fully compatible with legacy clients (such as Internet Explorer 6 on Windows XP).

To add Let’s Encrypt certificate to Heroku using SSL Endpoint add-on, simply enable the add-on and add the certificate:

$ heroku addons:create ssl:endpoint
Adding ssl:endpoint on example... done, v1 ($20/mo)
$ heroku certs:add /etc/letsencrypt/live/yourdomain.com/fullchain.pem /etc/letsencrypt/live/yourdomain.com/privkey.pem

You should get an output that looks somewhat like this:

Resolving trust chain... done
Updating SSL Endpoint bacon-42.herokussl.com for myapp... done
Updated certificate details:
Common Name(s): yourdomain.com
Expires At: 2016-03-10 18:48 UTC
Issuer: /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
Starts At: 2015-12-11 18:48 UTC
Subject: /CN=yourdomain.com
SSL certificate is verified by a root authority.

Use Heroku SSL (Beta) add-on

This is the new way to add a TLS certificate to your Heroku application. It uses Server Name Indication extension of TLS (SNI, for short) which is fully supported on all modern browsers. SNI allows provider (such as Heroku) to host multiple TLS certificates on a single IP address, cutting down the cost associated with provisioning a new instance for each certificate.

If you already running your dynos in any paid plan, you can enable this add-on and add the certificate for free following these steps:

$ heroku labs:enable http-sni
Enabling http-sni for bacon-pancake-42... done
$ heroku plugins:install heroku-certs
Installing plugin heroku-certs... done

You should get an output that looks somewhat like this:

Resolving trust chain… done
Adding SSL certificate to ⬢bacon-pancake-42... done
Updated certificate details:
Common Name(s): yourdomain.com
Expires At: 2016–03–10 18:48 UTC
Issuer: /C=US/O=Let’s Encrypt/CN=Let’s Encrypt Authority X1
Starts At: 2015–12–11 18:48 UTC
Subject: /CN=yourdomain.com
SSL certificate is verified by a root authority.

Congratulations! You have successfully installed Let’s Encrypt TLS certificate onto your Heroku application.

--

--

Senior Developer at Degica. I also contribute to open source projects, mostly in Ruby.