HTTPS/SSL on Heroku with Google Domains as DNS provider

This might be a bit of a niche share but it is something I struggled with recently. I have a Node.js application hosted on Heroku that I wanted deployed to https://www.mydomain.com. I bought my domain name through Google Domains as the DNS provider.

This means that even if the user puts in http://mydomain.com or http://www.mydomain.com it all needs to end up on https://www.mydomain.com. I’m not trying to belabor the point and it may seem obvious but it was non-trivial for me to set up the first time.

tl;dr — the website is hosted live here: http://www.linemansmilestones.com/ (it’ll forward the URL to the https version of the site)

Step 1: Google Domains

Initially I spent a lot of time messing with Google domains but I didn’t really need to. Here is the configuration I ended up with:

Google Domain DNS config for a heroku app

This is very similar to the official post on Google Domains Help for Map your domain to Heroku however the CNAME record is a bit different and the temporary redirect goes to thehttps:// version of my site.

Step 2: Let’s Encrypt

Update Oct ’17: Steps 2 and 3 could be combined with:
$ heroku certs:auto:enable

These two articles were very helpful for me in setting up the config:

Essentially you need to install certbot with homebrew and then run a command to manually generate a certificate. It sounds that scary but (now that it’s done) it’s not too bad.

$ sudo certbot certonly --manual

It’ll ask some questions and you get output like this:

Output after generating an SSL cert from Let’s Encrypt

In my Node.js app I added a route that server up that code as text. Below is the Node.js code.

server.js

router.get('/.well-known/acme-challenge/K_2RjEaeymUHc7iTWe-RdAFQjPLNdCdIuI1B2fnOpyk', (req, res)=>{
res.send('K_2RjEaeymUHc7iTWe-RdAFQjPLNdCdIuI1B2fnOpyk.8QUstrwiUKpP7oZH0ycw6DC0Y1dZwoHI7v5syvojMU8');
})

I’m not exactly sure what this would look like in Laravel, put if you do know please leave a comment.

Push your code up with that route to heroku and then verify. You now have fullchain.pem and privkey.pem files for your cert.

Step 3: Heroku

Now we’ve got Google Domains forwarding to Heroku and an SSL certificate. Next step is to let Heroku know about it all. For this to be supported you need at least the hobby plan on your heroku dyno so scale up:

$ heroku ps:resize web=hobby

There is also a certs plugin you need to install as well as add your domains to heroku. Those commands can be very simple:

$ heroku plugins:install heroku-certs
$ heroku domains:add www.linemansmilestones.com
Update 10/20/17: heroku certs is a core plugin of the heroku cli now so no need to install it.

You can check the domain is added at any time by running heroku domains.

That .herokudns.com address is what we configured our Google Domains CNAME record to point to!

In the above output from Let’s Encrypt you are given the full file paths to your fullchain.pem and privkey.pem files. Use those paths to add your certifications to Heroku.

$ sudo heroku certs:add /etc/letsencrypt/live/www.linemansmilestones.com/fullchain.pem /etc/letsencrypt/live/www.linemansmilestones.com/privkey.pem

Go have lunch. When you get back everything should have propagated so visiting https://yourdomain.com will render your application. (Of course you need to push your code using git push heroku master, a full intro to using heroku is available through their docs)

Step 4: Hacky, hack McHackerson

One bug I noticed is that heading to linemansmilestones.com successfully redirected to https://linemansmilestones.com but www.linemansmilestones.com in the URL bar stayed http://linemansmilestones.com.

This might have something to do with naked domain redirects and Google Domains not supporting ALIAS or ANAME records but I wasn’t really sure. Instead I found this client side solution that redirects http:// requests to your domain to https:// automatically

Drop this in the head section of index.html:

<script>
var host = "www.YOURDOMAIN.com";
if ((host == window.location.host) && (window.location.protocol != "https:")){
window.location.protocol = "https";
}
</script>

And that’s all folks! HTTPS is really important for processing payments. Pretty soon Chrome could show big ugly warnings for any HTTP address so it’s important to know how to configure domain names with SSL certs for your applications.

If you have any suggestions about how this workflow could be improved shoot me a tweet, email or leave a comment below.

Thanks for reading, happy hacking!