Bringing SSL to my Blog

Having a website being served over HTTPS used to be an expensive affair reserved only for the big sites and banks.

As time went on and the threats online grew, it became more and more common to see any website that handled personal details and especially payments to have it.

Over the past couple of years, the next phase in this evolution has been occurring: serving all sites over HTTPS no matter what they do.

Google has been favouring websites in search results, for the past couple of years and movements like LetsEncrypt has helped to lead over half of the Web of to now be encrypted.

So it was really inexcusable that 3 of the 4 websites that I currently host, were not yet secure. Granted, they are not dealing with personal details or sensitive data, but now that getting a certificate is now free, there is no good reason to make the switch.

Over the past couple of weeks, I made sure that they were all switched over. This blog was the last one to do.

For me, this was the hardest to switch as I was unfamiliar with the setup. I used Digital Ocean's pre-built server image to set this blog up so I was unsure exactly how it was configured. I knew it used nginx, which itself is not something I have used very much so would provide some challenge.

To make matters worse, Certbot doesn't have an automatic configuration maker for this setup, so you have to do it manually. With standard LAMP stack, the process is done with 1 command.

The post will hopefully be useful for anyone with a self hosted Ghost blog and even those using nginx for other purposes. I am using Ubuntu 16.04 and nginx version 0.91.

Step 1 - Install Certbot

The easiest way of using LetsEncrypt is by installing Certbot.

The installation instructions are easy to follow on the website, but here they are for completeness:

sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot 


You can now use it to install your certificates.

Step 2 - Install the Certificates

Before continuing, I made a copy of the vhost file as a backup.

Inside /etc/nginx/sites-enabled, if you run ls -la, you should see a single file called "ghost" pointing to another file in /etc/nginx/sites-available, also called "ghost". This is the server configuration that needs copying.

So first I did:

sudo cp /etc/nginx/sites-available/ghost /etc/nginx/sites-available/ghost-ssl

This duplicates the file and gives a new name. Now I want to use this copy instead of the original, so I first unlink:

sudo unlink ghost

Then create a new link with the same name as before but to the new file:

sudo ln -s /etc/nginx/sites-available/ghost-ssl ghost

At this point, if you restart nginx, the website and everything will behave exactly the same.

Now, we need to open the newly created file and add the following inside the "server" blog after the first "location" block:

location ~ /\.well-known/acme-challenge {
    root /var/www/ghost;

This is required by Certbot as this is the URL it will hit to authenticate the certificate. If you are like me and have a Ghost blog, it is likely that without this being there, going to that URL will cause a Ghost 404 error which will cause Certbot to fail.

Don't forget to restart nginx when you save:

sudo service nginx restart

Now you run:

certbot certonly

Certbot will ask you a series of questions.

First, how would you like to authenticate:

It is recommended you pick option 2 - webroot. This is why you need to to make the first adjustment to the vhost.

Next it asks you to Agree their terms and to enter the domains.

LetsEncrypt doesn't do wildcards, so you have to specify each domain and subdomain individually. I believe you can add subdomains later if you wish.

Next it was ask you to give the webroot for each domain:

After you have entered it the first time, you can keep selecting it (or add another) for the other domains. This should match the root you gave in the server config earlier.

After that, you should get a message saying that the certificates have been successfully installed and a path to their location. Keep a record of this for the next step.

Step 3 - Configure the Server

At this point, still nothing will change. We now need to tell the server that we want to server HTTPS pages and where to find the certificates.

I also want to go one step further and say that to redirect anything requested over HTTP to HTTPS.

To the server block that is already there, under the existing listens, I add:

listen   443 ssl;
listen   [::]:443 ipv6only=on ssl;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;

The privkey.pem will be in the same location as the certificate itself which is the location you got on the previous step.

Keep the rest of the block the same, as this is what was previously configured to work with Ghost.

Remove the two listen 80 lines and create a new server block above this one like so:

server {
    listen 80;
    listen [::]:80;
    return 301$request_uri;

This will cause any HTTP request (including from other domains other than, to get redirected to with the rest of the URL being preserved (e.g. the path to a specific blog post). It is given a 301 status code, which tells the browser that it is a permanent move.

One final thing that I am going to add (which is a bit extra) is to make sure that only the domains that I specify can access this site (this stops people accessing using the IP address or with any other domain name mapped to my IP address in their hosts files).

So to each server block I add the line:


I believe a "*" can be used to specify all subdomains. Then I create a third server block that acts as a "catch all" for all domains that are not named earlier:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen   443 ssl default_server;
    listen   [::]:443 ssl default_server;
    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    server_name _;
    return 301$request_uri;

So default_server says that for those ports, this is the default block to use if the server name is not matched elsewhere. As you can see, I catch all for both HTTP and HTTPS meaning that I have to specify the certificates again here.

Whatever is caught gets redirected to the HTTPS version of my site. If they happened to have a path to a blog post, this is also preserved.

And that is it. Restarting nginx will then immediately put these settings into action.

You may need to clear your browser cache before you see the change working.


So there you have it. This how you install SSL on your Ghost blog. It is reassuring to me now that my log in details are now encrypted and kind of cool that this is my first blog post that was encrypted before being sent to the server to be saved.

We are now one step closer to a fully 100% encrypted Web.

© 2012-2022