Ghost Blog Auto Setup with Nginx and ModSecurity

Ghost is a simple, modern WordPress alternative which puts the excitement back into blogging. It is beautifully designed, easy to use, completely open source, and free for everyone. Ghost can be run behind Nginx (as a reverse proxy) with ModSecurity for better performance and security. This blog is proudly published using Ghost.

ModSecurity Logo

"With over 70% of all attacks now carried out over the web application level, organisations need every help they can get in making their systems secure. Web application firewalls are deployed to establish an external security layer that increases security, detects, and prevents attacks before they reach web applications."
- From the ModSecurity project by Trustwave's SpiderLabs Team.

There are some good guides on the web about how to install/configure Ghost. Unfortunately, the information is fragmented and not all in one place. I am collecting them here, while adding my personal experience.

DEPRECATION NOTICE: This project is deprecated and has been archived. To learn more about the latest version of Ghost blog, see official repo on GitHub.

Getting a VPS

Before installing Ghost, you need to get a virtual private server (VPS) which can run Linux. A minimum of 512MB RAM is recommended.

DigitalOcean is the provider that I use. DigitalOcean also provides a pre-built Ghost app.

Note: If you like this article and want to support my site, please consider signing up using my referral links above.

Install and Configure Ghost

Note: Users upgrading their Ghost blog, follow these steps.

An excellent guide named Dude looks like a Ghost outlines the process of installing Ghost. For your convenience, I have compiled everything into this fully automated setup script, after adding important fixes and optimizations. It can be used with Ubuntu LTS, Debian 9/8 and CentOS 7/6. You may install multiple blogs on the same server.

Link to the script:

I try to keep the software versions up-to-date in the script above. For your information, the latest releases can be found at:

Ghost blog: Check for new versions at
Nginx: Official download page:

Users who prefer Naxsi over ModSecurity, see my other tutorial.

Set Up HTTPS for Your Blog

You can get a FREE SSL certificate and set up HTTPS for your blog!

Let’s Encrypt is a new Certificate Authority (CA) that is free, automated, and open. Sponsored by major companies such as Mozilla, Cisco and Facebook, Let's Encrypt is officially leaving beta on April 12, 2016.

Here is a list of client implementations for Let's Encrypt. Besides the official client, the more popular ones include Get HTTPS for Free, No Sudo Client, Simp LE and ACME Tiny. Read the documentation, visit the community, and check out this tutorial. It is worth mentioning that the Caddy web server, written in Go, has built-in support for Let's Encrypt.

StartCom is another well-known CA providing free SSL certificates. To get started, follow this guide. Note that the step(s) to generate your private key and Certificate Signing Request (CSR) can be simplified to:

openssl req -new -newkey rsa:2048 -nodes \

The output file YOUR.DOMAIN.NAME.csr will contain your CSR. Continue with the guide above, and at the "Submit CSR" page, copy that file's content and paste into the text box. Proceed with the remaining steps, and follow the official instructions to install the certificate. For security, make sure your private key YOUR.DOMAIN.NAME.pem is only readable by root.

Sitemap, Robots.txt and Extras

Note: Newer Ghost blog versions now automatically generate a sitemap and robots.txt. If you wish to instead create and use your own files, uncomment the corresponding location block in nginx-blog1.conf and restart Nginx.

To generate a sitemap for your blog, look at Ghost sitemap generator, which provides a script for this. Change url to your blog's domain name, and if you followed the first guide above, you need to edit webroot to e.g. /var/www/YOUR.DOMAIN.NAME/public (create that public folder first). After running the script, your sitemap.xml will be generated inside it.

Adding robots.txt to your site is easy. A basic example is:

User-agent: *  
Disallow: /ghost/  
Disallow: /signout/  
Sitemap: http://YOUR.DOMAIN.NAME/sitemap.xml  

Put this file under /var/www/YOUR.DOMAIN.NAME/public. Refer to my nginx.conf below. The same config block can also be used for site verification html files from e.g. Google Webmaster Tools, just place them under public and add the filenames to that location regex.

There's a nice trick to let your blog's links open in a new tab or window, just add " target="_blank before the closing ). In addition, if you want to add Disqus comments to your posts, see this guide by Christoph Voigt. Browse to this post by George Liu for how to load comments on demand as well as add comment counts to post titles.

Want to add Google Analytics to your blog? It's easy. After signing up to get tracking code, edit the file content/themes/casper/default.hbs and put it just before the </head> tag, then restart Ghost. Also want to track outbound links that your visitors click on your blog pages? No problem, see this great how-to. To use that code "inline", remember to wrap it in <script>...</script> tags.

Finally, if you use DigitalOcean to host your blog, learn how to set up your hostname or domain with DigitalOcean's DNS.

My Nginx Configuration

An example nginx.conf is shown below. To save it, click on "view raw" at bottom-right corner, Ctrl-A to select all, Ctrl-C to copy, then paste into your favorite editor. Remember to replace every YOUR.DOMAIN.NAME with your blog's full domain name. If you don't need SSL, comment out listen 443 and all lines starting with ssl_.

Also, here is the actual Nginx configuration for this blog, with various optimizations added. This is for your reference only.

Setting Up E-Mail on Ghost

To configure email, read the official instructions. Remember to restart Ghost. If using Amazon SES, try this alternative configuration (credit):

mail: {  
    transport: 'SMTP',
 // For a list of all Amazon SES SMTP endpoints, see:
    host: 'ssl://',
    options: {
        port: 465,
        service: 'SES',
        auth: {
            user: 'YOUR_SES_SMTP_CREDENTIAL_ID',

Upgrading Your Ghost Blog

To upgrade your Ghost blog, see: How to upgrade Ghost blog.

Follow the instructions below ONLY IF you had used my script to install Ghost. Replace every YOUR.DOMAIN.NAME with your actual domain name.

# Stop running Ghost process  
su - ghost -s /bin/bash  
forever stopall  
# Make a Backup. VERY IMPORTANT! cd /var/www/ cp --archive YOUR.DOMAIN.NAME YOUR.DOMAIN.NAME-OLD
# Create temp dir, download the ghost source and unzip cd YOUR.DOMAIN.NAME mkdir temp cd temp/ wget unzip
# Replace files and set correct owners cp *.js *.json *.md LICENSE ../ cd .. chown ghost:ghost *.js *.json *.md LICENSE rm -rf core cp -R temp/core ./ chown -hR ghost:ghost core rm -rf content/themes/casper cp -R temp/content/themes/casper content/themes/ chown -hR ghost:ghost content/themes/casper

Not done yet! Now is a good time to go into your YOUR.DOMAIN.NAME-OLD backup folder and look for files you had customized before, e.g. files in content/themes/casper. Manually add those customizations to your new Ghost install in YOUR.DOMAIN.NAME. Then edit the config file config.js as needed. When finished, proceed with upgrading Ghost:

# Upgrade Ghost  
su - ghost -s /bin/bash  
cd /var/www/YOUR.DOMAIN.NAME/  
npm install --production
# If you see no error, start Ghost blog process ./ exit
# Remove temporary dir when finished rm -rf /var/www/YOUR.DOMAIN.NAME/temp

Congratulations! You have successfully upgraded your Ghost blog.

Please share this post if you like it, and do not hesitate to write your comments or questions in the Disqus form below.

Next article: Install PBX in a Flash (PIAF) on a DigitalOcean Droplet
Previous article: Scripts for Auto IP Updates on Amazon EC2 or DigitalOcean

Return to Lin's Tech Blog Homepage

Lin Song

Hi, I'm Lin, a PhD graduate in Electrical and Computer Engineering. As a hobby, I love computers, Linux and programming.  LinkedIn  GitHub

View or Post

Disclaimer: All content provided on this blog is for informational purposes only. The owner of this blog makes no representations as to the accuracy or completeness of any information on this site or found by following any link on this site. All trademarks mentioned herein belong to their respective owners.
    The owner of this blog will not be liable for any errors or omissions in this information nor for the availability of it. The owner will not be liable for any losses, injuries, or damages from the display or use of this information.

Your name:

Email address:

Website URL:

Please leave a comment:

You agree that this form is for A N T I-S P A M B O T S!
     D O-N O T-S U B M I T !