After reading entirely too many articles and a lot of trial & error, I've finally managed to get HTTPS working on my DigitalOcean-powered, Nginx-backed Ghost blog using LetsEncrypt's free SSL certificates. For years people have argued whether it's worth setting up SSL on websites that have no inherent need for strong security (credit card processing, user logins, etc), but now that SSL certificates have next to no cost and Google is
Having [regretfully] setup SSL certificates on Apache servers numerous times in the past, it's not at all surprising that this process was a headache to sort out, though it seems like LetsEncrypt is on the right path to simplifying the process with its command line toolkit (which regrettably doesn't have an automated install for Nginx quite yet). Fortunately, it does provide one-line commands to get a folder of bare certificates for both single or multiple domains that works on any server.
All steps are written from the viewpoint of a Ghost blog that was setup on DigitalOcean using it's one-click installer, but they're likely applicable to most Nginx/Ghost setups in general. Regardless you should probably be familiar with using a terminal and SSH — if you aren't I highly recommend brushing up via those links before you get started.
- If you already have a Ghost blog setup then skip to step 2. If not, go ahead and create a new droplet on DigitalOcean using the Ghost “One-Click App” install (Ghost 0.7.9 on Ubuntu 14.04 as of this writing) with 512mb of memory1 (the $5/month option). Feel free to choose whichever datacenter is closest to yourself (or your users) and add an SSH key so you can access your Droplet via your local terminal.
- Once the droplet is finished building, access your droplet over SSH via the terminal2 of your choosing with
ssh firstname.lastname@example.org, where
0.0.0.0is the IP address of your droplet as listed in your account. If it prompts you to trust the RSA key type
ENTER. Long-term you should probably disable the root user, but that's outside the scope of this guide.
- Once you're inside your server (synonymous with “droplet”), run
apt-get update && apt-get upgrade -yto bring all Linux dependencies up to date.
- Next go ahead and open your Nginx configuration with
vim /etc/nginx/sites-available/ghostand update it to match the block below — available as a gist here — replacing the six (6)
yourdomain.cominstances accordingly (if you have an older droplet this file may not exist, in which case you can either edit
/etc/nginx/conf.ddirectly or set this up yourself).2
service nginx restartto restart Nginx and adopt the new settings. If you get a
nginx -tto automatically detect where the error occurred.
cd /opt && wget https://dl.eff.org/certbot-auto && chmod a+x certbot-auto. This will copy the repository from LetsEncrypt's mirror on the EFF website and set the correct permissions for it to run safely.3
/opt/certbot-auto certonly --webroot -w /var/www/ghost -d yourdomain.com -d www.yourdomain.com, replacing “yourdomain.com” in both entries. Fill out your email address and agree to the TOS if prompted, and otherwise sit back and sip a cup of coffee while it does its thing. This will create a hidden
.well-knownfolder within your
/var/wwwdirectory and check against it from the LetsEncrypt servers. Note: Your website/server must be publicly accessible over port 80 for LetsEncrypt to verify your domain.
- Remove or comment out the first 12 lines of the Nginx config, and then uncomment (remove
#signs) from the second half of the settings in
/etc/nginx/sites-available/ghost, then run
service nginx restartagain to adopt the new settings.4
https://yourdomain.comto make sure that SSL is working properly.
- Lastly, you'll want to set up a cronjob to automatically renew the certificate every 60 days or so, otherwise they'll expire after 90 days. Go ahead and run
crontab -ein your server terminal, and you should see something similar to the image below. On the last line add
0 0 1 */2 * /opt/certbot-auto renew --quiet --no-self-upgrade, which tells cron to rerun the certificate renewal for you on the first day of every other month.
Redirecting From non-www to www Instead
I've personally always favored non-www addresses, but here's instructions to redirect to
www. prefixed URLs instead. In short all you need to do is reverse the www/non-www redirects by following the instructions below. Here's an alternative gist that shows how it should end up after removing the first temporary block from lines 1-12.
www.into the URL on line 17.
www.from the URL on line 22 and add
www.to line 23.
www.into the URL on line 37.
- August 11, 2016: Updated guide to add steps breaking apart enabling HTTP, verifying LetsEncrypt, and enabling HTTPS.
- October 19, 2016: Updated the Nginx config to have the default root directory as
/var/www/ghostinstead of just
- January 24, 2017: Updated the Nginx config to fix a bug where www to non-www redirects wouldn't occur properly on HTTPS URLs, and added instructions on how to redirect from non-www to www instead.
- July 3, 2017: Removed lines 59-61 in the Nginx config Gist and code snippet, which previously manually routed the robots.txt and sitemap.xml, as Ghost now handles these automatically for the past several major versions. Huge thanks to both Tom Blackshire & Grant Whinney for both giving me the heads up!
If you're worried about only having 512mb of memory feel free to upgrade, though this blog has handled upwards of 650 concurrent visitors without skipping a beat. ↩
The new first block will 301 redirect all non-HTTPS requests, the second block will 301 redirect all HTTPS requests if a user manually enters a www URL, and the third is the actual block routing all requests to Ghost after redirects are finished. ↩