SSL for Rails with Heroku and Let’s encrypt
Tags: rails, heroku, ssl
Published: 2020-07-21 00:40:52

SSL certificates have been finally made common thanks to **Let’s encrypt**. 

Since I originally wrote this article, Heroku released [Automated Certificate Management (ACM)](https://devcenter.heroku.com/articles/automated-certificate-management) which should be the preferred way to do this task. But for the sake of brain exercising, here is the original article.

Let’s see how we can configure a Rails app hosted on Heroku with a Let’s encrypt generated certificate. The second part can be applied to any certificate.

Replace `example.com` with your actual domain 😉

## Create the page to verify your domain

At this step, I assume you already have an up and running Rails application on Heroku.

You need to add the page to serve the private key given by Let’s encrypt to validate the ownership of your domain.

Create a controller or use one you have already with the following public method:

```ruby
def letsencrypt
  render text: "#{params[:id]}.#{ENV['LETS_ENCRYPT_KEY']}"
end
```

In your `config/routes.rb` file, add a route to the page you just created.

```
get '/.well-known/acme-challenge/:id', to: "pages#letsencrypt", constraints: { id: /[a-z0-9_-]+/i }
```

## Get Let’s encrypt certificate

We need to get Let’s encrypt binaries first. In a working directory:

```bash
git clone https://github.com/letsencrypt/letsencrypt
```

Then just go in the folder and use `letsencrypt-auto` binary. It’s in development, so you still need to use the `--debug` flag. Also, to protect your certificate, you need to run the binary as `root` user, with `sudo` for instance.

```bash
sudo ./letsencrypt-auto certonly -d your.domain  --debug
```

Set the environment variable `LETS_ENCRYPT_KEY` to match the private key given by Let’s encrypt binary, that the part given after the `.`. Here for instance, when you get prompted

```bash
Make sure your web server displays the following content at
http://your.domain/.well-known/acme-challenge/pA0ucRCnGPnG6S-0fVF93A_-0CQb_rSfeDOYvAXh8Ck before continuing:

pA0ucRCnGPnG6S-0fVF93A_-0CQb_rSfeDOYvAXh8Ck.Yq3zvhj7vmfBveGvR85p4nwlOBtf7gip40sSrif__Rr
```

the private key is `Yq3zvhj7vmfBveGvR85p4nwlOBtf7gip40sSrif__Rr`. In a second terminal, in your Rails app folder, do the following:

```bash
heroku config:set LETS_ENCRYPT_KEY=Yq3zvhj7vmfBveGvR85p4nwlOBtf7gip40sSrif__Rr
```

You can then continue with the Let’s encrypt process by pressing ENTER.

Now the binary is requesting a certificate via letsencrypt.com and the Authority is checking that your domain is yours by accessing via http protocol, the page `http://your.domain/.well-known/acme-challenge/pA0ucRCnGPnG6S-0fVF93A_-0CQb_rSfeDOYvAXh8Ck`.

You should get a Congratulations message.

## Configure Heroku

Assuming you already installed the Heroku CLI, add the ssl endpoint add-on:

`heroku addons:create ssl:endpoint`

Add your new fresh certificate to Heroku:

`sudo heroku certs:add /etc/letsencrypt/live/your.domain/fullchain.pem /etc/letsencrypt/live/your.domain/privkey.pem`

Learn more about [SSL endpoints in Heroku doc](https://devcenter.heroku.com/articles/ssl-endpoint).

## Reconfigure your DNS

Ask Heroku what is the ssl endpoint your application got

```bash
heroku certs
```

You get a list like that:

```bash
Endpoint                  Common Name(s)  Expires               Trusted
------------------------  --------------  --------------------  -------
endpoint-0.herokussl.com  example.com     2016-01-01 00:00 UTC  True
```

Change now your DNS entry to

```
IN CNAME endpoint-0.herokussl.com.
```

Learn more about [configuring your DNS in Heroku doc](https://devcenter.heroku.com/articles/custom-domains#configuring-dns-for-root-domains).

## Test your configuration

After your new DNS entry got propagated, you should be able to access https://example.com with a valid secured connection.

If you want to test it before, you can access directly https://endpoint-0.herokussl.com and check the certificate being properly served.

🍻 Peace!

<small>_Originally published on February 24, 2016_</small>

<small><i>Photo by <a href="https://unsplash.com/@jeisblack?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jason Blackeye</a> on <a href="https://unsplash.com/s/photos/lock-door?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a><i><small>