I transitioned from Apache to Nginx a week or so ago, since words like “minimal” and “streamlined” are appealing to me ;). I was quite happy with Apache, but it's always nice to try something new. Anyhow here's a quick review my configuration.

On Gentoo, set the the modules you want to install by adding the following lines to your /etc/make.conf:

NGINX_MODULES_HTTP="access auth_basic autoindex charset fastcgi gzip gzip_static limit_req map proxy rewrite scgi ssi stub_status"
NGINX_MODULES_MAIL=""

Then install Nginx with:

# emerge -av nginx

Make any adjustments you like to /etc/nginx/mime.types. I added:

types {
    …
    application/x-python                  py;
    application/x-shell                   sh;
    …
}

Now it's time to setup /etc/nginx/nginx.conf. Poking about online will give you lots of examples. Here are things that were useful to me, in the order they appear in the http block of my nginx.conf.

Redirecting www.example.com to example.com

This keeps people who accidentally add a www. prefix to your URL from matching the wildcard virtual host block defined below.

server {
  # www.example.com -> example.com
  listen 80;
  listen 443 ssl;
  server_name www.example.com;
  ssl_certificate /etc/ssl/nginx/www.example.com.pem;
  ssl_certificate_key /etc/ssl/nginx/www.example.com-key.pem;
  rewrite  ^/(.*)$  $scheme://example.com/$1  permanent;
}

Gitweb (and general CGI approach)

gitweb server:

server {
  listen 80;
  server_name git.example.com;

  access_log /var/log/nginx/git.example.com.access_log main;
  error_log /var/log/nginx/git.example.com.error_log info;

  root /usr/share/gitweb/;

  index gitweb.cgi;

  location /gitweb.cgi {
    include fastcgi_params;
    fastcgi_pass  unix:/var/run/fcgiwrap.sock-1;
  }
}

Because Nginx lacks built-in CGI support, we need some tricks to get gitweb.cgi working. We use the fcgi module to pass the requests on to a FastCGI server which wraps gitweb.cgi. On Gentoo, I installed the following packages:

  • www-misc/fcgiwrap, a FastCGI server for wrapping CGI scripts
  • www-servers/spawn-fcgi, a FastCGI manager for spawning fcgiwrap.

Configure spawn-fcgi to launch fcgiwrap with:

# cp /etc/conf.d/spawn-fcgi /etc/conf.d/spawn-fcgi.fcgiwrap
# emacs /etc/conf.d/spawn-fcgi.fcgiwrap
# cat /etc/conf.d/spawn-fcgi.fcgiwrap
FCGI_SOCKET=/var/run/fcgiwrap.sock
FCGI_ADDRESS=
FCGI_PORT=
FCGI_PROGRAM=/usr/sbin/fcgiwrap
FCGI_USER=nginx
FCGI_GROUP=nginx
FCGI_EXTRA_OPTIONS="-M 0700"
ALLOWED_ENV="PATH HOME"
HOME=/
FCGI_CHILDREN=1
FCGI_CHROOT=
# cd /etc/init.d/
# ln -s spawn-fcgi spawn-fcgi.fcgiwrap

Start fcgiwrap with:

# /etc/init.d/spawn-fcgi.fcgiwrap start

Add it to the default runlevel with:

# rc-update add spawn-fcgi.fcgiwrap default

If you also want a virtual host serving Git over HTTP, you can add a virtual host like:

server {
  # http-git.example.com
  listen 80;
  server_name http-git.example.com;

  access_log /var/log/nginx/http-git.example.com.access_log main;
  error_log /var/log/nginx/http-git.example.com.error_log info;

  location / {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend;
    fastcgi_param GIT_HTTP_EXPORT_ALL "";
    fastcgi_param GIT_PROJECT_ROOT /var/git;
    fastcgi_param PATH_INFO $document_uri;
    fastcgi_pass  unix:/var/run/fcgiwrap.sock-1;
  }
}

This uses the same FastCGI server we used for gitweb, but this time the backend CGI script is git-http-backend.

Wildcard virtual hosts

To add support for virual hosts stored under /var/www/$host, use:

server {
  listen 80;
  #listen 443 ssl;

  server_name star.example.com *.example.com;

  access_log /var/log/nginx/star.example.com.access_log main;
  error_log /var/log/nginx/star.example.com.error_log info;

  #ssl_certificate /etc/ssl/nginx/$host.pem;
  #ssl_certificate_key /etc/ssl/nginx/$host.key;

  root /var/www/$host/htdocs;

  # deny access to .htaccess files
  location ~ /\.ht {
    deny all;
  }
}

Then adding a new host is as simple as creating a new entry in /var/www/ and updating your DNS to get the new name pointed at your server. Unfortunately, SSL/TLS doesn't work with this approach. It appears that certificates and keys are loaded when Nginx starts up, but $host is only defined after a request is received. Nginx does support SNI though, so it will work if you write SSL entries by hand for hosts that need them.

Main host

The configuration for my main host is more complicated, so I'll intersperse some more comments. I setup both clear-text and SSL in the same definition using the SSL module. The _ server name is a special name that matches any requests which haven't already matched and been handled by an earlier server.

server {
  # catchall virtual host (optional SSL, example.com)
  listen 80 default_server;
  listen 443 default_server ssl;
  server_name _;

  ssl_certificate /etc/ssl/nginx/example.com.pem;
  ssl_certificate_key /etc/ssl/nginx/example.com-key.pem;

Nothing special with the logging or root.

  access_log /var/log/nginx/example.com.access_log main;
  error_log /var/log/nginx/example.com.error_log info;

  root /var/www/example.com/htdocs;

Turn on SSI, and also use index.shtml as index pages.

  index index.html index.shtml;
  ssi on;

Use the proxy module to pass requests for /cookbook/ and subdirectories on to their underlying Django app.

  location /cookbook/ {
    proxy_pass  http://localhost:33333/cookbook/;
    proxy_set_header  X-Real-IP  $remote_addr;
  }

Use the scgi module to pass requests for /gallery/ and subdirectories on to their underlying SCGI app.

  location /gallery/ {
    include scgi_params;
    scgi_pass localhost:4000;
  }

Turn on autoindexing for /RAD/ and subdirectories using the autoindex module.

  location /RAD/ {
    autoindex on;
  }

Force SSL/TLS for /tree/ and subdirectories, redirecting plain-text requests to the equivalent HTTPS page. Use the auth_basic module for authentication, the SSL module for $ssl_protocol, and the rewrite module for the redirection.

  location /tree/ {
    auth_basic "Family Tree";
    auth_basic_user_file /home/jdoe/htpasswd;
    if ($ssl_protocol = "") {
      rewrite ^   https://example.com$request_uri? permanent;
    }
  }

Nothing special with the end of this server block.

  # deny access to .htaccess files
  location ~ /\.ht {
    deny all;
  }
}