I recently double-checked my nginx configuration against the one that Elastic Dog has so proudly featured. I’m very glad that I did – they provided me with a better understanding of the if / test capabilities of the syntax.

That being said, it still needed some adjustments …


I’m currently running WordPress 2.7 under nginx 0.7.27. Here’s my end configuration:

I’ll provide the content of extra/proxy.conf and fastcgi_params down below – they won’t surprise you – plus the configuration for my upstream fastcgi_cluster.

The purpose of DOMAIN.NAME and LOGFILE is obvious, so let’s skip to the useful stuff.


This is simply the fully-qualified path to the directory where you have installed WordPress. Big shock, I know. I put mine in ‘/var/www/wordpress’.


Specifically, I’m referencing the ‘WordPress address (URL)’ capture block.

Wordpress 2.7 supports a differentiation between the root context of your blog and the root context of the WordPress resources themselves. I’ve taken this approach … the URL of this blog post is root-relative to my virtual hostname, but if you do a View Source you’ll see:

<link rel="stylesheet" href="http://blog.cantremember.localhost/wordpress/wp-content/themes/cantremember/style.css" type="text/css" media="screen" />
<!-- ... -->
<link rel="pingback" href="http://blog.cantremember.localhost/wordpress/xmlrpc.php" />

It’s a nice-to-have, and in many ways allows the configuration to be somewhat easier. In WordPress Admin, under Settings :: General, I have configured:

  • WordPress address (URL) = http://blog.cantremember.localhost/wordpress
  • Blog address (URL) = http://blog.cantremember.localhost

So my WP-CONTEXT is ‘wordpress’.


Here are the core differentiations between my config and the Elastic Dog one:

My core two sections are the ones with WP-CONTEXT. Before doing anything, I make the $request_filename context-less, so that it’s corrected relative to root. Granted, I could have skipped that step because I used ‘wordpress’ for each, but that doesn’t make for as good an example, and regex’s aren’t that expensive (don’t they have dedicated chips for them by now?).

I was having issues when WordPress wanted to take me to the Admin screen. It used the shortcut ‘/WP-CONTEXT/wp-admin’, which is great if you’re not doing all this fancy re-writing and fastcgi_index can take over. But we are being fancy. That’s why the $request_filename/index.php text exists. It works like a charm, although there may be a more efficient way to do this.

And here is where it became an advantage to differentiate between blog URLs and WordPress resources. I’ve chosen to make my permalinks dateless – /%postname%/ . Call me crazy, but I like the way it looks on Laughing Squid. Given that’s the case, it’s hard to differentiate between ‘/some-permalink/’ and ‘/wp-admin/’. Splitting them off with the ‘wordpress’ context made this possible.

The final context-less ‘Blog address (URL)’ capture block is exactly what you’d expect.


I liked the simplicity and capabilities of PmWiki 2.2.0. It’s an easier decision, since I have no intention of being a grand-scale collective document facility. PmWiki is a powerful and flexible implementation with a lot of great processing directives that you can embed in a page. Yet that also makes security something of a concern (as some reviewers will point out as well). Global multi-tier password auth is available, and user-based auth is available as necessary.

This configuration is a natural extension of the WordPress one above:

Everything here is obvious, including /PATH/TO/PMWIKI-DIR. Mine is ‘/var/www/pmwiki’. Here’s the lowdown:

In the *.php capture block, you’ll see that the default script is pmwiki.php. I had created a symlink to rename it index.php, but after my config re-adjustment, that became obsolete.

The non-existing file test will be triggered by the following requests:

  • /Main/HomePage
  • /Main/HomePage?action=edit

Those URLs exist because I’m leveraging a feature called $EnablePathInfo. The referenced documentation doesn’t do it justice … this feature allows me to have bare Group/Name URLs, much like I’m doing with my bare blog URLs. I’ll just say that I’m being SEO-minded and leave it at that.

Turning on that feature informs PmWiki to generate the URLs in that format, and it also makes the PHP script capable of parsing the CGI headers to do-the-right-thing. My original configuration required me to perform the following override hack:

include  fastcgi_params;
fastcgi_param SCRIPT_NAME '';

But the revised configuration above simply re-writes the URL into the standard ?n= format and the script never has to deal with CGI headers. The only other rewrite considerations were to transform any querystring ‘?’ into ‘&’ and to remove the leading ‘/’ from the Group/Name combo.


Supporting Configuration

For all means and purposes, I’m using nginx’s default fastcgi_params.

This is extra/proxy.conf, derived from their NginxFullExample, with notes-to-self intact:

This is fastcgi_cluster, which is just a simple example of how to do clustering:

I tried using some of the additional server setting features – commented out above – but they weren’t working in my build of 0.7.27. I can live without them at the moment, but the upstream block capabilities are quite powerful.

I’m running 3 FastCGI instances, each with 5 worker threads. Again, good enough for government work. I custom-built fcgi on OS X, but for my AWS Fedora Core 8 image I just went with spawn-fcgi that comes along with the lighttpd package.

This cluster config is also a nice starter reference for adding load-balancing capabilities to external AWS instances. Given the volatile nature of VM image mappings, I’ve split the cluster config off into its own file for scripted generation.