Nginx FastCGI Example (with PHP FPM) – Path_Info & PHP_SELF

There are many guide about configuring nginx with PHP FPM, but many of them are incomplete (doesn’t handle PATH_INFO correctly) or contain security issues (doesn’t check whether the script is indeed php file).

FastCGI Params

First thing, I recommend keeping all your typical FCGI settings in a single file and importing them.

For example on debian and ubuntu by default there is /etc/nginx/fastcgi_params file that should looks 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_FILENAME         $document_root$fastcgi_script_name;
fastcgi_param   SCRIPT_NAME             $fastcgi_script_name;
fastcgi_param   PATH_INFO               $fastcgi_path_info;
fastcgi_param 	PATH_TRANSLATED		    $document_root$fastcgi_path_info;
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;
fastcgi_param   HTTPS                   $https;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param   REDIRECT_STATUS         200;

Connecting nginx to PHP FPM

Now we must tell Nginx to proxy requests to PHP FPM via the FCGI protocol:

        location ~ [^/]\.php(/|$) {
                fastcgi_split_path_info ^(.+?\.php)(/.*)$;
                if (!-f $document_root$fastcgi_script_name) {
                        return 404;
                fastcgi_index index.php;
                include fastcgi_params;

If you’re using unix socket change fastcgi_pass to:

                fastcgi_pass unix:/var/run/php5-fpm.sock;

Restart nginx.


Create test.php on nginx document root containing just:

 <pre><?php var_export($_SERVER)?></pre>

In the browser try to request:

  1. /test.php
  2. /test.php/
  3. /test.php/foo
  4. /test.php/foo/bar.php
  5. /test.php/foo/bar.php?v=1

Pay attention to the value of REQUEST_URI, SCRIPT_NAME, PATH_INFO and PHP_SELF.

Here’s the correct output for http://lemp.test/test.php/foo/bar.php?v=1 :

array (
  'USER' => 'www-data',
  'HOME' => '/var/www',
  'QUERY_STRING' => 'v=1',
  'CONTENT_TYPE' => '',
  'SCRIPT_FILENAME' => '/var/www/test.php',
  'SCRIPT_NAME' => '/test.php',
  'PATH_INFO' => '/foo/bar.php',
  'REQUEST_URI' => '/test.php/foo/bar.php?v=1',
  'DOCUMENT_URI' => '/test.php/foo/bar.php',
  'DOCUMENT_ROOT' => '/var/www',
  'SERVER_SOFTWARE' => 'nginx/1.4.0',
  'REMOTE_ADDR' => '',
  'REMOTE_PORT' => '44644',
  'SERVER_ADDR' => '',
  'SERVER_PORT' => '80',
  'SERVER_NAME' => '',
  'HTTPS' => '',
  'REDIRECT_STATUS' => '200',
  'HTTP_HOST' => 'lemp.test',
  'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:20.0) Gecko/20100101 Firefox/20.0',
  'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.5',
  'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
  'HTTP_CONNECTION' => 'keep-alive',
  'PHP_SELF' => '/test.php/foo/bar.php',
  'REQUEST_TIME' => 1367829847,


  1. The location regex capable to handle PATH_INFO and properly check that the extension indeed .php (not .phps) whether there is PATH_INFO or not.
  2. The fastcgi_split_path_info regex capable to correctly handle request like /test.php/foo/blah.php or /test.php/.
  3. The if lets nginx check whether the *.php does indeed exist to prevent nginx to feeding PHP FPM non php script file (like uploaded image).
  4. Some guides recommend to use try_files instead of if, if you do that, beware of nginx bug #321. I personally think if is more appropriate for this, even IfIsEvil agree this is one of the 100% safe thing to use if with.

This guide run fine on php.ini cgi.fix_pathinfo = 1 (the default). Some guide insist to change it to cgi.fix_pathinfo = 0 but doing that make PHP_SELF variable broken (not equal to DOCUMENT_URI).

Leave a Reply

Your email address will not be published. Required fields are marked *