De Organic Design
Nginx by all accounts is much more efficient than Apache, so on 1 July 2013 we migrated our server and server installation procedure over to Nginx.

Nginx uses an asynchronous event-driven approach to handling requests, instead of the Apache model that defaults to a threaded or process-oriented approach. Nginx's event-driven approach can provide more predictable performance under high loads.

Another reason we're moving over to Nginx is due to the recent interest in Perfect forward secrecy (PFS) coming from articles such as this. PFS is an obscure feature of SSL/TLS and requires at least OpenSSL version 1 and Apache version 2.3.3, but Nginx has supported it for quite some time now.

Quote.pngApache is like Microsoft Word, it has a million options but you only need six. Nginx does those six things, and it does five of them 50 times faster than Apache.
Chris Lea


All our local installation documentation is in the install a new server procedure. The selecting a good set of ciphers section covers more detail about the perfect forward secrecy issues and installation. Note that the cgi.fix_pathinfo directive which used to be a critical security patch for Nginx has been redundant since about PHP version 5.3.

Our URL rewriting rules

The OD server uses a rather complicated URL-rewriting system that allows all the wikis under all domains to run from a singe "catch-all" server block - or actually two, one for plain and one for SSL. This was quite difficult to replicate on Nginx such that it could work in exactly the same way and thereby be "web server agnostic".

The catch-all wiki rewrite rules apply if no other domain-based patterns have matched such as requests with an svn or webmail sub-domain prefix. Our configuration is rather "if" heavy which is strongly discouraged by Nginx gurus, but they're ok as long as they're not inside location context or contain only rewrite last directives or other non-content-handling operations such as set. Our ones here that are inside location context contain only set directives which should be safe. See this article for more detail about how the "if" directive works and why it can be so tricky to use in practice.

This first block sets variable called $wiki which will be used by other following conditions and location blocks (note that even if the .php block is included prior to these settings in the file, they are actually evaluated after them as these blocks are all outside of location scope. $wiki is the directory in which the wiki's file structure resides, i.e. one of the symlinks in /var/www/domains which are named to match the domain of the request. It is used in the following rules in the main scope and also by the nginx.php.conf include for setting the fastcgi_params. Note that this condition always applies and sets $wiki.

if ($host ~* ^(www\.|wiki\.)?(.+)$) {
     set $wiki /$2;

Then our first rewrite rule matches the root request with no path or file specified which gets rewritten to the wiki Main Page.

rewrite ^/$ $wiki/wiki/index.php?title=Main_Page&redirect=no last;

Next we need to check if the request is for an image thumbnail with dynamic sizing and if so, route the requests to the thumb.php script. This is simpler than on Apache because Nginx doesn't have the ampersand bug that requires an extra rule for dealing with thumbnails for filenames containing the ampersand symbol.

rewrite ^/files/thumb/./../(.+?)/(\d+)px- $wiki/wiki/thumb.php?w=$2&f=$1 last;

Otherwise if the uri points to an existing file we use that, or as an overall default we treat the request as a friendly URL by rewriting to the script with the URI in the title query-string item. (we used to use the PATH_INFO form, but this fails for article that end in .php).

try_files $wiki$uri $wiki$uri $wiki/wiki/index.php$request_uri;

Stand-alone and local wikis

For wikis that have their own code-base directory instead of using the shared code-bases of the wiki farm can use a block similar to the following example to do their friendly URL's.

server {
    listen 80;
    include /var/www/conf/nginx.php.conf;
    root /var/www/foo
    rewrite ^/$ /wiki/index.php?title=Main_Page&redirect=no last;
    rewrite ^/wiki/images/thumb/./../(.+?)/(\d+)px- /wiki/thumb.php?w=$2&f=$1 last;
    try_files $uri $uri /wiki/index.php$request_uri;

Block processing order

  • try_files only does a redirect for the last parameter so others cannot be *.php as the php location won't be processed
  • Only one location block will match and be processed
  • The first exact match (using =) will return immediately
  • Next strings will be matched, the most specific match being chosen
  • Then regex matches will be chosen the first match overriding any string matches (strings can use ^~ to block the regex tests after a match)
  • Rewrites at server level are evaluated before the location directives are evaluated
  • Rewrites within location blocks are then evaluated
  • If rewrites within a location block change the URI, then the location directives are evaluated again

See also