Debug with PHP
First of all, in modern PHP, the PATH_INFO
is stored in the $_SERVER
array. Try:
echo "called SCRIPT_NAME: {$_SERVER['SCRIPT_NAME']} with PATH_INFO: {$_SERVER['PATH_INFO']}";
In any case phpinfo()
comes to the rescue to help find a lot of the internal php information, like variables and configurations.
Nginx config
As for the NginX config most of it is already explained in the other posts. So this here is a summary and a closer look at the details and the why of the following sample location block:
location /main.php {
# regex to split $uri to $fastcgi_script_name and $fastcgi_path_info
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
# set the standard fcgi paramters
include fastcgi.conf;
# pass the request to the socket
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
Explanation line-by-line
The fastcgi_split_path_info
splits your location between SCRIPT_NAME
and PATH_INFO
.
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
The expression in the first parentheses of the regular-expression extracts the SCRIPT_NAME
, while the second extracts the PATH_INFO
.
Recap on regular-expressions
The first regex group, (.+?\.php)
, expects any character (the dot .
), at least once or more than once (the plus +
). with a trailing .php
. The dot in .php
is escaped to \.php
so it's taken literally not as "any character".
The questionmark ?
makes the plus lazy (+?
) so the evaluation stops at the first .php
suffix.
- E.g. -
/some.php/next.php/path-info
is evaluated to a SCRIPT_NAME
of /some.php
with a PATH_INFO
of /next.php/path-info
; beware, not to a SCRIPT_NAME
of /some.php/next.php
with a PATH_INFO
of /path-info
.
The second regexp group, (/.*)
, basically takes everything that start with a slash as PATH_INFO
.
The leading ^
and trailing $
bind the expressions to the start and end of the line.
The next line checks that the extracted script really does exist as a file:
try_files $fastcgi_script_name =404;
Otherwise it returns a 404
error. This prevents giving non-existing files to the PHP processor, however has the bad habit of resetting the $fastcgi_path_info
variable (see: http://trac.nginx.org/nginx/ticket/321).
One workaround is to store $fastcgi_path_info
in $path_info
and set the FCGI param to the stored $path_info
. This is done by the next two lines:
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
The other FCGI parameters are then set within the include of fastcgi.conf
. This file, that's sometimes also named fastcgi_params
should be provided by your distribution.
include fastcgi.conf;
Then finally pass the request to your current PHP instance socket (here PHP 7.4):
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
Conclusion
Now remember that all of this happens only, if the surrounding location block is hit. The above example is a prefix location, meaning that every location is matched, that starts with the prefix /main.php
. This would be a typical configuration for a routed PHP application that has only one central file named main.php
. To catch all .php
files a regex has to be used, which could be as simple as ^.+?\.php(/|$)
. The (/|$)
after the .php
means that there's either a slash (and more characters) or nothing after the .php
part of the location. Subdirectories are also allowed, so the expression matches basically every location that somewhere contains the string .php
, as long as it's either at the end or followed by a slash.
location ~ ^.+?\.php(/|$) {
#...
}
As the location is only the guard that allows entering the following block, the final PHP filename and path-info are still split as described above. If the resulting filename does not exist a 404 is returned.
This is just a simple configuration. Of course there's a myriad of possibilities to configure the location regex, to suit the needs of your specific application. To go into all that details would be a small book.