Over the last couple of years I’ve been constantly researching for a way to get the PHP environment variables to show up correctly. My latest pains were with PATH_INFO and PHP_SELF, which are now finally solved.
My current configuration are PHP-FPM (5.2.10) and NginX (0.8.29) on a CentOS 5.4 x64 VPS.
Traditionally you would use a PHP configuration such as this:
server {
server_name your-domain.com www.your-domain.com;
location / {
root html/default;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html/default$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}Within fastcgi_params would be something like this:
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; # PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200;
While the above method may seem to work at first, you’ll quickly notice problems when it comes to using $_SERVER['PATH_INFO'] and $_PATH['PATH_TRANSLATED'], and often enough $_SERVER['PHP_SELF'] ends up being set incorrectly when you try to adjust for the two environment variables.
Here is a setup I’ve come to prefer, especially when it comes to having multiple virtual hosts that use PHP. Notable tips are commented below the line.
Simple Nginx configuration file with a single virtual host
worker_processes 1; pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; index index.html index.htm index.php; # Identical to Apache's DirectoryIndex, setting it in # the http block set it as a default for all server blocks within server { listen 80; # since port 80 is set by default, you do not actually need # to set this, unless of course you are binding to a specific # address such as listen server-ip-address:80 or alternate port; server_name gyn0.com www.gyn0.com; root html/default; # You will want to set your root here, since otherwise # $document_root within the php block will not work # if you set it in the location block you would also have # to set the php block within that location as well location / { # Below are some wordpress-friendly rewrite rules if (-f $request_filename) { expires 30d; break; } if (!-e $request_filename) { rewrite ^/.+/?(/wp-.*) $1 last; rewrite ^/.+/?(/.*\.php)$ $1 last; rewrite ^/(.+)$ /index.php?q=$1 last; } } location = /favicon.ico { access_log off; log_not_found off; } # If you haven't created a favicon for your site, you can keep # your access and error logs clean by turning off the logs # when a browser requests the fav icon (its also a good way # to keep your logs from filling with useless information) include php; # I prefer to keep my php settings in one file, so I can simply # paste this single line for each of my virtual hosts } }
Now the php file (which I’ve created in the /conf folder with nginx.conf)
fastcgi_intercept_errors on; # this will allow Nginx to intercept 4xx/5xx error codes # Nginx will only intercept if there are error page rules defined # -- This is better placed in the http {} block as a default # -- so that in the case of wordpress, you can turn it off specifically # -- in that virtual host's server block location ~ \.php { fastcgi_split_path_info ^(.+\.php)(/.+)$; # A handy function that became available in 0.7.31 that breaks down # The path information based on the provided regex expression # This is handy for requests such as file.php/some/paths/here/ include fastcgi_params; # Includes the modified fastcgi parameter file (see below) fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; }
The new modified fastcgi_params:
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; #SCRIPT_NAME needs to remain without the document_root on front #Otherwise PHP_SELF breaks, the interpreter will still find the file #because of the added SCRIPT_FILENAME parameter below fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #This one was added right into the file, since with root moved, and path_info split #It should no longer need to be set manually for each virtual host fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; #the two lines below are possible now because of the fastcgi_split_path_info function fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx; # I removed the version, as not to give away too much information fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; #PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200;
There you have it, PHP should now have correct environment variables. For example http://www.kbeezie.com/php.php/a/path/string/?var=foo would render the following results (the url above doesn’t actually work, sorry
):
$_SERVER["QUERY_STRING"] -> var=foo $_SERVER["SCRIPT_NAME"] -> /php.php $_SERVER["SCRIPT_FILENAME"] -> /usr/local/nginx/html/default/php.php $_SERVER["REQUEST_URI"] -> /php.php/a/path/string/?var=foo $_SERVER["DOCUMENT_URI"] -> /php.php/a/path/string/ $_SERVER["DOCUMENT_ROOT"] -> /usr/local/nginx/html/default $_SERVER["PATH_INFO"] -> /a/path/string/ $_SERVER["PATH_TRANSLATED"] -> /usr/local/nginx/html/default/a/path/string $_SERVER["PHP_SELF"] -> /php.php/a/path/string/
So there you have it. A simple php block that will correctly assign the path environment variables, without having to use multiple blocks and patterns. And quite easy to simply assign to a new virtual host by simply pasting the include php; line.

First of all, thanks for sharing this! I’ve successfully replicated your setup, and for /php.php/a/path/string/?var=foo I get the same results as you. I do have a question however:
What should the expected result be if php.php is in a subfolder, i.e. /folder/php.php/a/path/string/?var=foo?
Currently, PATH_TRANSLATED for /php.php/ and /folder/php.php/ is exactly the same, I wonder if this is indeed correct or if /folder/ should go in there too.
When using paths after the php file name, PATH_INFO should show up as the path trailing the script, I would assume this should be the same for PATH_TRANSLATED as well when using a path trailing the file name. I do however advise that people concentrate on parsing the REQUEST_URI as opposed to relying on PATH_INFO and PATH_TRANSLATED. Most systems now days use mod_rewrite or similar and use the request_uri instead of the older path_info.
Also ran into a PHP security issue after following this guide (mainly because my php.conf did not include the folder restrictions anymore I had in place before).
http://cnedelcu.blogspot.com/2010/05/nginx-php-via-fastcgi-important.html
Yes, I’d prefer mod_rewrite over file.php/something too but unfortunately there are still a lot of scripts around that do this, both in Open Source and Proprietary scripts.