Nginx, PATH_INFO, and url-rewriting: i must be missing something

I'm switching from Apache to Nginx/fcgi and I'm having a small issue while attempting to setup the applications rewrite rules.

The code handles routes via the use of PATH_INFO, e.g., example.com/foo/bar/ will be routed to example.com/index.php/foo/bar/

I've modified the server configuration to pass PATH_INFO:

location ~ \.php$ {
      include /etc/nginx/fastcgi_params;
      fastcgi_pass   127.0.0.1:9000;
      fastcgi_index  index.php;

      fastcgi_split_path_info ^(.+\.php)(.*)$;
      fastcgi_param SCRIPT_FILENAME  /var/www/public$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
}

And the rewrite handler:

location / {
      root   /var/www/public;
      index  index.html index.htm index.php;

      if (!-f $request_filename) {
        rewrite ^(.*)/(.*)$ /index.php/$2 break;
      }
    }

With 'rewrite_log on', the url's appear to be routing properly:

[error]: *2 open() "/var/www/public/index.php/test" failed (20: Not a directory), client: 192.168.0.254, server: example.com, request: "GET /test HTTP/1.1", host: "example.com", referrer: "http://example.com/index.php"

However, it appears to be looking for the directory "test". How can I force it to request index.php, passing '/test' to the script?


Looks like you're following a poor tutorial which is severely outdated (if (!-f is generally not recommended, with try_files offered as a better replacement). You have numerous problems:

First you have directives in location / context which should be in server context, so you can avoid path duplication in the SCRIPT_FILENAME variable.

Secondly, you use the break flag in your rewrite; this means it should not re-evaluate the location matching. This effectively makes the request never leave the location / block and is instead treated as a static file request.

Thirdly, have a look at your location ~ \.php$ {: Nginx matches locations against the URI and you're specifically telling Nginx to only handle URIs which end in .php, but if you rewrite to index.php/test/ then it won't trigger.

Lastly, you're using PATH_INFO while you should be using REQUEST_URI. The difference is that to make PATH_INFO work you have to tell Nginx to pass any request with .php in it to PHP, and PHP has to then find the right file.

This means that you allow requests like example.org/uploads/image.jpg/index.php to be sent to PHP and you also allow PHP to execute the file /uploads/image.jpg. If a malicious user uploaded that file with PHP in it they now have arbitrary code execution on your server. This is a very real exploit that I have personally seen people be vulnerable to.

Please stop using randomly Googled tutorials because 90% of them are utter garbage, instead go read the wiki. It might be a bother having to actually learn about the web server you're using, but the alternative is to have your server compromised.