Octopress and Nginx

This is an old post. It may contain broken links and outdated information.

Getting Octopress set up took a bit more time than I was expecting, because of the missing libraries issues I encountered. Fortunately, actually putting Octopress published and visible on the web took a lot less time.

The Bigdinosaur.org web server runs the latest stable version of Nginx, an event-driven web server which serves static pages faster than a fat kid eats pancakes. Nginx has a roughly similar concept of virtual hosts as Apache does, and because Octopress doesn’t require any databases and doesn’t use PHP or perl, it can be served with a very simple site definition file.

Most folks tend to be comfortable doing virtual hosts in the Apache way. If you install Nginx on Ubuntu from a repository (there is a Launchpad PPA with the latest stable release, which is several revisions newer than the one in the default sources), you’ll end up with an /etc/nginx/sites-available directory and an /etc/nginx/sites-enabled directory, with the former containing site definition files and the latter containing symlinks to those files, just like Apache. We’ll use those directories here.

Configuration 1: HTTP only

To do a site without HTTPS, we’ll need to create a single file:

$ sudo touch /etc/nginx/sites-available/yourblog.com

Add the following inside that file:

server {
    ### This setting tells Nginx to use this configuration if it gets a request for
    ### yourblog.com
    server_name yourblog.com;

    ### This is the location on the web server where your Octopress files are
    ### published. Setting this here means you don't have to set it for any of the
    ### individual locations you define below.
    root /var/www/yourblog.com;

    ### This tells Nginx to use "index.html" as the default index page everywhere
    index index.html;

    ### This disables automatic directory index creation, since no one will be
    ### browsing your directories anyway
    autoindex off;

    ### Here we define the root location... 
    location / {
    ### ...and then work some magic with "try_files", telling Nginx that for every
    ### request that comes in to /, it should first try to serve the URI exactly
    ### as it is, and if it doesn't find anything by that name to then try and
    ### serve the URI as a directory, and if it doesn't find a directory by that
    ### name to then spit out a 404 error and give up.
        try_files $uri $uri/ =404;
    }

    ### This location definition prevents Nginx from serving any files which begin
    ### with a dot, and further to not log any access attempts or 404s for files
    ### which begin with dots, to keep your access and error logs clean.
    location ~ /\. {
        access_log off;
        log_not_found off;
        deny all;
    }

    ### This location definition prevents Nginx from serving any files which begin
    ### with a dollar sign, so Nginx will refuse to serve out a temp file if you
    ### are doing any editing inside a web-available directory
    location ~ ~$ {
        access_log off;
        log_not_found off;
        deny all;
    }
    ### These next two locations simply prevent Nginx from logging every time the
    ### favicon & robots.txt files are accessed, to keep the logs clean
    location = /robots.txt {
        access_log off;
        log_not_found off;
    }

    location = /favicon.ico {
        access_log off;
        log_not_found off;
    }
}

Easy! Then, enable the site by symlinking it into the sites-enabled directory and have Nginx re-parse its config files to make it live:

$ sudo ln -s /etc/nginx/sites-available/yourblog.com /etc/nginx/sites-enabled/yourblog.com
$ sudo /etc/init.d/nginx reload

Configuration 2: HTTP and HTTPS

But what if you’ve got some fancy-pants SSL/TLS certs that you want to use so that you can read your blog via an encrypted connection? This involves a slightly different way of configuring the server. Typical practice with Apache is to have one configuration file for an HTTP host and a separate config file for an HTTPS host, but with Nginx we can use one configuration file for both. Plus, to keep from having to type a bunch of stuff out twice and keep up with it twice, we’ll put the configuration directives and locations that both servers share into a separate file and tell Nginx to parse that, too.

This time, we’ll need two files created:

$ sudo touch /etc/nginx/sites-available/yourblog.com
$ sudo touch /etc/nginx/common.conf

In the site definition file, add the following:

### The first server block is the HTTP server
server {
    server_name yourblog.com;
    root /var/www/yourblog.com;
    index index.html;
    autoindex off;

    ### And now, rather than list a bunch of locations here, we just tell Nginx to
    ### go find the "common.conf" file and read that instead. We're going to add
    ### stuff into common.conf in just a sec.
    include common.conf;	
}

### The second server block is the HTTPS server, which has the "listen 443"
### line to tell Nginx that traffic for it will come on TCP port 443. The
### HTTP server above doesn't have a "listen 80" line because when the "listen"
### directive is omitted, "listen 80" is implied.
server {
    server_name yourblog.com;
    root /var/www/yourblog.com;
    listen 443;
    root /var/www-blog;
    index index.html;
    autoindex off;
    include common.conf;

### The next set of directives tells Nginx that this server uses SSL, and then
### supplies it with the configuration settings necessary to make SSL work
    ssl on;
    ssl_certificate /path/to/your/ssl/certificate.crt;
    ssl_certificate_key /path/to/your/ssl/privatekey.pem;
    ssl_session_timeout 5m;
    ssl_protocols SSLv3 TLSv1;
    ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP:!kEDH;
    ssl_prefer_server_ciphers on;	
}

Note the ssl_ciphers line excludes the extremely secure but extremely DSE-RSA-AES256-SHA algorithm (that’s the !kEDH on the end of the line), which will speed up SSL/TLS connection speed by quite a bit, but at the cost of some encryption strength. For a bit more on why I’ve added this here, see this page over on the blags of matt. Note also that Nginx doesn’t support separate certificate chain files—you will need to concatenate your server certificate and whatever upstream certificates needed into a single chained certificate file and use that.

Next, add this into the common.conf file:

### This file contains common configuration and location directives for the
### HTTP and HTTPS versions of my Octopress blog

### Common root location
    location / {
        try_files $uri $uri/ =404;
    }

### Common deny, drop, or internal locations
    location ~ /\. { access_log off; log_not_found off; deny all; }
    location ~ ~$ { access_log off; log_not_found off; deny all; }
    location = /robots.txt { access_log off; log_not_found off; }
    location = /favicon.ico { access_log off; log_not_found off; }

Then, just like above, symlink that puppy in and have Nginx read it.

$ sudo ln -s /etc/nginx/sites-available/yourblog.com /etc/nginx/sites-enabled/yourblog.com
$ sudo /etc/init.d/nginx reload

And then, you are officially on-line! I’ll do a follow-up post this evening or tomorrow on custom error pages, which are also fun.