Nginx
Nginx by all accounts is much more efficient than Apache, so we will probably start changing the OD server, and our server installation procedure over to Nginx. Currently (as of June 2013) we're running both Apache and Nginx with the former on ports 80 and 443 and the latter on 8080 and 8989. This way I can get the entire file structure of all sites running properly on Nginx without any change to the live sites on the standard ports. When everything is working I can switch Nginx over to the standard ports and then stop Apache and eventually un-install it completely if Nginx works out well.
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.
Contents
Installation
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.
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".
Som important things to remember about Nginx request processing are:
- 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 thenn evaluated
- If rewrites within a location block change the URI, then the locations is evaluated again
- $request_uri is the original request, $uri is updated after rewrites
SERVER_NAME
To make a catch-all type system I've used the default_server option at the end of the listen directive and ommitted the server_name directive since its value is a literal copy of any wild-card or regular expression value it's given. To ensure we don't have an empty SERVER_NAME value in the PHP environment, I've set it to $host in our specific PHP fastcgi_params settings in /var/www/work/nginx.php.conf.
Fixing PATH_INFO
The first issue I came across is that there seems to be a problem with the PATH_INFO system where Nginx can't handle URLs that request a script with a continues path such as /foo.php/bar?biz. I used KBeezie's solution which almost works, but it uses the fastcgi_split_path_info function which also seems to have a problem. It's supposed to accept a regular expression containing two captures, one gets assigned to the $fastcgi_script_name variable, and the other to $fastcgi_path_info, but for some reason the former doesn't contain the path portion of the script even if the regular expression states that it should. So in our version of KBeezie's PHP configuration I've used the following custom variables instead of $fastcgi_script_name and $fastcgi_path_info.
Note: In addition to these changes, we need to set $wgUsePathInfo to true in some MediaWiki versions when using Nginx, for example it was needed in MediaWiki 1.19.2, but not in 1.20.3. There's no harm (in our wikia environment) in having it always set though so this has been added to our wikia.php extension.
Fixing SCRIPT_NAME
This code block is at the start of our /var/www/work/nginx.php.conf file which is included from any of our server blocks that require PHP processing. But the block still required further adjusting as the SCRIPT_NAME parameter in this configuration was not matching what Apache was giving and this was causing Nginx to keep redirecting indefinitely. The problem is that SCRIPT_NAME includes the hostname e.g. /organicdesign.co.nz/wiki/index.php instead of just /wiki/index.php that Apache's SCRIPT_NAME would contain. This seems to be getting added to the $document_root value internally somehow, but only for the setting of SCRIPT_NAME. So the initial patch at the start of nginx.php.conf is now as follow:
This effectively removes the beginning portion of $script if it starts with the same value as $wiki (which is set in the main server block to the name of the domain excluding www or wiki prefixes). The effected fastcgi_param names are now set as follows:
Note: The MediaWiki configuration variable $wgServer seems to be getting incorrectly set sometimes when left to the default, so I've now set this manually in our wikia.php extension.
The rewrite rules
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 directives. Our ones here that are inside location context contain only set directives so hopefully this avoids the dangerous "if" pitfalls that often occur in the Nginx world.
The first blocks set some variables 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. This one sets the $wiki value which 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.
The next is the $script variable which is the script name without PATH_INFO part but including prior path elements for example /wiki/index.php.
Then our first rewrite rule matches the root request with no path or file specified which gets rewritten to the wiki Main Page.
Note, we need a couple more rules here for handling thumbnails which will be added soon.
If the requested URI maps to an existing file in the wiki structure, then we rewrite it to point to that.
Otherwise as an overall default we treat the request as a friendly URL by rewriting to the script with the URI in the PATH_INFO for the wiki to evaluate an article title from.
FastCGI parameters
The minimum required fastcgi parameters that allow a proper PHP request to be carried out are: