3/31/2011 This has been updated to reflect a better configuration to be used with Nginx 0.8/0.9.
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. (As of 2011, I’m now using PHP 5.3.6, Nginx 0.9.6 and FreeBSD 8.2)
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 example.com www.example.com; root html/example.com/; # 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 / { # This would replace the typical mod_rewrite rules for wordpress # it can also be try_files $uri $uri/ @rewrites; where it goes to a # location @rewrites { ... } where you can place rewrite rules if a file # or folder is not found. try_files $uri $uri/ /index.php; } 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) location ~ /\. { access_log off; log_not_found off; deny all; } # You want to make sure that Nginx does not serve any .hidden files include php.conf; # 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.conf 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/ fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; 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 SCRIPT_FILENAME $document_root$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; 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; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; }
There you have it, PHP should now have correct environment variables. For example http://www.example.com/php.php/a/path/string/?var=foo would render the following results:
$_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. 🙁
I replicated your config but my scripts were returning a 404 in the error log. I had to have a SCRIPT_FILENAME manually configured for each vhost which solved the problem.
Well that’s not really *solving* it but rather taking a long work around. If you have the root defined in the server { } block and not location { } block then script filename would be automatic in the fastcgi params.