Securing your Self-Hosted Ghost 5.20 instance with NGINX

I just set up blog.riamaria.com a few days ago. It's been really fun setting up the server. I looked up some ways to secure my Ghost instance better. Great ideas here and there, and mostly just regular AppSec things.

With this guide, I assume that Ghost handled the NGINX configs for you upon installation. Here are some things that you can do with NGINX to harden your Self-Hosted Ghost instance:

<aside> 💡 Note that you after saving the NGINX config files, you should run nginx -t before reloading NGINX ( server reload nginx ) to propagate changes gracefully.

</aside>

Redirect from HTTP to HTTPS

Unfortunately, the default Ghost instance will allow http://yourblog.example to go through to your site. I hope at this point in time people know better than to go to HTTP only sites due to lack of encryption in-transit.

As root, edit /etc/nginx/sites-enabled/yourblog.example.conf:

 location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass <http://127.0.0.1:2368>;
 }

to

location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        return 301 https://$host$request_uri;
}

This will permanently redirect you from HTTP to HTTPS if you go to the HTTP site.

Removing HTTP headers

One reason to not publish the NGINX version in the response headers of your site is to make it a little more difficult for attackers to sniff what version you are running on. Running on a known vulnerable version will make it more likely for bad actors to try and do something funny on your instance.

As root, edit /etc/nginx/nginx.conf and uncomment server_tokens off;. It should look like this:

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        types_hash_max_size 2048;
        server_tokens off;

Another thing is that you might want to remove the X-Powered-By: Express header as well. In both /etc/nginx/sites-enabled/yourblog.example.conf and /etc/nginx/sites-enabled/yourblog.example-ssl.conf, add the following line: proxy_hide_header X-Powered-By;. It should look like this:

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        return 301 https://$host$request_uri;
        proxy_hide_header X-Powered-By;
    }
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass <http://127.0.0.1:2368>;
        proxy_hide_header X-Powered-By;

    }

Restrict Admin Access to your IP only