nginx + php-fpm - (defaults + documentation) = :(
Today I had to kickstart a web server to host an upcoming project I'm working on. I thought to myself, "hey, I've used Apache almost exclusively in the past, but I hear good things about nginx, let's give it a shot." Well, a few hours later and I'm regretting the decision a bit.
Overall, installing the relevant packages (nginx, php, php-fpm) on CentOS via yum, with the addition of the Software Collections (SCL) Repository for PHP 5.4, was pretty painless. There are quite a few handy tutorials around the web to walk through basic setup and configuration. However...
At first glance, the default configuration for nginx provided via the CentOS package looks like it covers the basics. Unfortunately, it's full of very questionable defaults, which only becomes apparent once you dig through the documentation to figure out what they mean. For example, it includes some commented out configuration for fast-cgi (php-fpm):
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #}
The fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; line looks straightforward enough until you realized /scripts is an absolute path. What happens is that any php script URIs matching the location expression get appended to the /scripts path. The assumption is that you've got a root directory called scripts that will house all your php scripts - I can't believe that's standard practice. A better option is changing the line toΒ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;, as suggested by most tutorials and the nginx common pitfalls document. Now the script name is appended to the document root as expected.
Next up is theΒ root html; line. It is confusing because it sets the document root to a relative path called html. Relative to what, you might ask? According to official documentationΒ it is relative to whatever parameters nginx was compiled with, or specifically --prefix=path. If this isn't set, it defaults to /usr/local/nginx. For whatever reason, the CentOS nginx package I was using appears to default to /etc/nginx (which I only discovered after getting phpinfo() working). Again, not very intuitive.
My last headache was digging into the "Passing Uncontrolled Requests to PHP" issue outlined on the nginx pitfalls page. A bug was discovered circa 2010 that noticed you could get arbitrary files to get passed through the php interpreter when using the default nginx configuration with php-fpm. In brief, if someone uploads a file to /myFile.jpg and then requests the path /myFile.jpg/fake.php, php/myFile.jpg gets passed through the php interpreter. Voila, compromised web server. Most sites recommend setting cgi.fix_pathinfo=0 modifying the nginx configuration to explicitly block this. Turns out that since PHP 5.3.9, released in January 2012, the php-fpm configuration contains a security.limit_extensions option enabled by default (set to .php). What it does is prevent anything without the matching extension from getting executed, mitigating the bug. Of course, the official php-fpm documentation contains no mention of it, and every tutorial I looked at insisted that cgi.fix_pathinfo=0 was the way to go. Alas...
The power of open source software is an amazing and wonderful thing, but it really gets marred by a poor understanding of the people who attempt to use them. Maybe it's a (possibly elitist) culture of keeping things complicated so that people 'earn' the right to use them, but I can't believe all those wasted hours are so necessary. /rant












