nginx has been pleasantly stable – the only time it has gone down is when I misconfigured it into infinte HTTP 302 redirects, and even then it took several hours of user activity to knock it over.
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 …
WordPress
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.
/PATH/TO/WP-DIR
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’.
WP-CONTEXT
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:
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:
Blog address (URL) = http://blog.cantremember.localhost
So my WP-CONTEXT is ‘wordpress’.
Usage
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.
PmWiki
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.
Hooray!
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.
NOTE: If your screen reader is reading this, please contact me at admin@cantremember.com ... because it shouldn't.
FIXME: build this dynamically based upon the maximum content in any sub-Element of this Element.
I will call this my "Safari Reader Counterweight".
In some of my Posts, I have huge code excerpts, etc.
Safari Reader, at least in iOS, will identify the 'main Element', the one it features, based upon its content length.
Sometimes those code excerpts get identified as the 'main Element', and the Post is borked in Safari Reader mode.
This is a counter-weight; it gives the <main> Element additional content so that it gets featured, algorithmically.
Yes, it increases the payload of every page (@see FIXME above).
But not by that much.
Then again, this is a guess as to how much content any given Element could contain.
If it's not enough, BOOM, Safari Reader looks like crap.
So, here's a great article on how to enable Safari Reader on your site.
It's mostly guesswork, but those guesses helped me debug this obtuse goddamn problem.
Oh, and look, you can enter and exit Reader programmatically.
JavaScript can fix anything.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.
I promise I will never cut-and-paste lines of text simply to add Element payload.