Image for post
Image for post

How to set up SSL with Let’s Encrypt on Heroku for free

Edit: Heroku now provides Let’s Encrypt SSL for Hobby dynos for free out of the box. Just enable it in your app’s settings. Things described in this guide still work, but it’s no longer needed to do all these steps.

It’s possible to configure a SSL certificate using various Heroku plugins, but this guide is for Heroku SNI, which is free, if you have at least Hobby dyno (7$/mo).

Heroku SNI and its configuration is described in their documentation, but I want this guide to cover all steps. You shouldn’t need to look elsewhere.

Disclaimer: I consider myself rather a designer than a developer, so this may not be the perfect guide, but this is how I did it and it works 🤘.

I am using Ruby on Rails for the backend, but this can be easily used for any other framework/language.

Setting this up is a multi-step process. I will assume that you plan to use a custom domain for you project. You need to:

  1. Turn on Heroku SSL
  2. Add your custom domain to your Heroku app
  3. Generate the certificate on your machine
  4. Make your app respond to certbot
  5. Upload the certificate to Heroku
  6. Set up your app for SSL

Let’s get our hands dirty.

1. Turn on Heroku SSL

In order to start using SNI SSL on your application, you first need to enable the labs flag and install the CLI plugin.

Update: Heroku released a stable version, so no labs flag needed! I’ll leave it here for a future reference though.

On your machine:

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

If all goes well, you can now find a “SSL Certificates” section in the Heroku settings of your app. Looks like this:

Image for post
Image for post
SSL Certificates section of your Heroku app settings

2. Add your custom domain to your app

DNS changes always take some time to come through. We can use that time to…

3. Generate the certificate on your machine

If you’re on mac, it’s really simple:

$ brew install certbot

For other systems, check https://certbot.eff.org/.

With your certbot installed, fire it up:

$ sudo certbot certonly --manual

It will ask for your email address first. You know what to do. After that, fill in your custom domain for the project.

The process continues with one prompt, but after confirmation, you will see something like this:

Make sure your web server displays the following content athttp://your.domain/.well-known/acme-challenge/first-part-of-string-random-characters before continuing:first-part-of-string-random-characters.-second-part-of-string-random-characters

now STOP!

Notice how the string you have to display has two parts, divided by a period. See, I don’t have the necessary knowledge, but I messed this up a few times and noticed something. The first part changes for every certificate you generate and is sent in the request. The second part stays the same and only you know it. We will use this in the next step.

Leave your terminal open in this state and switch to your code editor.

4. Make your app respond to certbot

# Let’s encrypt
get ‘/.well-known/acme-challenge/:id’ => ‘pages#letsencrypt’

I used my static pages controller for this, but you can use whichever you want. In your controller of choice, add this method:

def letsencrypt
render text: “#{params[:id]}.-second-part-of-string-random-characters”
end

You can store the second part of random characters in some ENV variable for extra safety. That’s it for now, publish your code.

The DNS changes should be ready by now, so your app should be accessible without issues. You will see some warnings if you try the https address though.

5. Upload the certificate to heroku

Now, you can use Heroku’s interface in settings for this part, but I used a CLI plugin we installed in the beginning.

$ sudo heroku _certs:add /etc/letsencrypt/live/<your.domain>/fullchain.pem /etc/letsencrypt/live/<your.domain>/privkey.pem — app <your-app>

You will see some kind of confirmation and when you visit your Heroku settings now, you should see “Your certificate your.domain expires on …”.

And that’s it! Well… for the most part. When you visit you site at https://your.domain you will see some issues.

6. Set up your app for SSL

  • the site was still available on the http address
  • assets were loading from http host

First issue can be solved simply by having this in your config/environments/production.rb:

# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.config.force_ssl = true

The second issue depends on how you store your assets, but don’t forget to check this, as you won’t get a green lock unless it’s all secure.

Bonus: Automate the certificate renewal

I found a little app called Sabayon, that claims to do this, but haven’t tried it yet. Check it out.

Thank you for reading, I hope this guide helped you somehow. If you have any questions, I probably won’t have the answers, but feel free to ask 😁.

Should Designers Code?

Coding experiences of a designer. Guides, stories, reviews.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store