Implementing SSL isn’t difficult but it is a little fiddly. Most guides to SSL tell you what to do but they don’t explain why. Happily SSL in Plain English does and it’s a great help to understand what you’re doing. Obviously!
Here I’ll explain what I did and why, but not how. Other people have already done that so I’ll link to them where appropriate.
How much SSL?
The first decision is whether to have SSL for every page or just some of them. I chose SSL across the site: it’s simpler and securer.
Since we’re using SSL everywhere, we can enforce it at the webserver level. This has two advantages:
- We don’t need to change the app server or app.
- We can test our webserver’s HTTPS configuration while continuing to serve the app over HTTP.
As you can see, the app server and the app itself don’t need to know we’re using SSL to talk to the outside world.
The SSL certificate
My goal was to secure the transport link between my site and the outside world; I wasn’t interested in verifying the identity of my site.
I wanted a certificate which supported the root domain (
example.com) as well as subdomains (
*.example.com). It turns out not all subdomain certificates also support the root domain.
So I was after a wildcard certificate but not an “extended validation” (EV) certificate.
- RapidSSL: doesn’t support root domain according to sales guy I spoke to
- DigiCert: recommended by DynDNS, my DNS provider, but expensive ($595/year)
- DNSimple: only supports domains registered or hosted with them
- GoDaddy: ugly site but works
By a process of elimination I plumped for GoDaddy’s “Single Domains with Unlimited Subdomains (Wildcard)” certificate. Buying it was straightforward, as you’d expect, but finding it in my GoDaddy account was unnecessarily difficult.
The web server (Nginx)
While my site continued to run over HTTP, I added HTTPS configuration and tested until it was correct. These sources were invaluable:
Once everything was working over both HTTP and HTTPS, I was able to cut over to HTTPS-only by redirecting all HTTP traffic to HTTPS, and adding the HTTP Strict Transport Security (HSTS) header.
The app server (Unicorn)
I didn’t need to change anything here.
The app (Rails 3)
Since I was enforcing SSL at the web server level, there was no need to use Rails'
I updated my links to use protocol-relative URLs instead of HTTP-specific ones.
I renamed the session cookie so that existing users were forced to get a new one (secured by the HSTS header).
And I updated my asset host to use a scheme-appropriate link to my CloudFront CDN.
If instead you decide to implement SSL in your app rather than your webserver, you’ll find these articles helpful:
The CDN (CloudFront)
Since my CloudFront distribution was already set to match the protocol of the viewer, I didn’t need to change anything.
Amazon CloudFront CDN on Rails is still my preferred article on setting up CloudFront.
The monitoring (Pingdom)
Just about the only glitch I ran into was with Pingdom.
I updated my Pingdom check’s Required Settings to use HTTPS instead of HTTP, only to find it started falsely reporting the site was down. It turned out the Optional Settings still had the port explicitly set to 80. Manually changing this to 443 fixed the check.
Almost every SSL and Rails guide I read described how to configure Rails to implement SSL. But if you go with SSL for your entire site, you can push the SSL out to where it really belongs: the web server.
Finally I recommend testing your SSL with Chrome because I found it has the most sensitive and visible warnings.