Reputation: 333
I configured nginx stable (1.4.4) + PHP (using FastCGI, php-fpm) on Debian. That works fine:
location ~* ^/~(.+?)(/.*\.php)$ {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
alias /home/$1/public_html$2;
fastcgi_pass unix:/var/run/php5-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_index index.php;
autoindex on;
}
I use the PATH_INFO variable, therefore I added the following line to fastcgi_params:
fastcgi_param PATH_INFO $fastcgi_path_info;
And in /etc/php5/fpm/php.ini:
cgi.fix_pathinfo = 0
I think that should work, but when I print out all server variables, PATH_INFO is always empty:
array (
'USER' => 'www-data',
'HOME' => '/var/www',
'FCGI_ROLE' => 'RESPONDER',
'QUERY_STRING' => '',
'REQUEST_METHOD' => 'GET',
'CONTENT_TYPE' => '',
'CONTENT_LENGTH' => '',
'SCRIPT_FILENAME' => '/usr/share/nginx/html/srv_var.php',
'SCRIPT_NAME' => '/srv_var.php',
'PATH_INFO' => '',
'REQUEST_URI' => '/srv_var.php',
'DOCUMENT_URI' => '/srv_var.php',
'DOCUMENT_ROOT' => '/usr/share/nginx/html',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'GATEWAY_INTERFACE' => 'CGI/1.1',
'SERVER_SOFTWARE' => 'nginx/1.4.4',
.....
)
I can not figure where the problem is. Any ideas?
Upvotes: 13
Views: 23849
Reputation: 410
Reference: https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_split_path_info
location ~ .+\.php {
fastcgi_pass {your_php_fpm_sock_or_server};
# Some old (custom) frameworks use "PATH_INFO" for routing, requiring the following 2 settings. (e.g. index.php/Home/index)
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
Upvotes: 0
Reputation: 1130
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.
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;
}
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
.
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.
/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;
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.
Upvotes: 6
Reputation: 664
I have come across this issue but my scenario is slightly different as I use try_files
in my directive. Here is my config along with technical explanations:
This is what my location
block looks like
location / {
include php-fpm.conf;
try_files $uri $uri/ /index.php =404;
}
and php-fpm.conf
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+?\.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
set $path_info $fastcgi_path_info;
include fastcgi.conf;
fastcgi_param PATH_INFO $path_info;
Two special notices here:
nginx
via brew
and it did not contain PATH_INFO
param so I had to add it manually (taken from here)fastcgi_param PATH_INFO $fastcgi_path_info;
try_files
is a special case (source)The try_files directive changes URI of a request to the one matched on the file system, and subsequent attempt to split the URI into $fastcgi_script_name and $fastcgi_path_info results in empty path info - as there is no path info in the URI after try_files.
INFO_PATH
to a temporary variable and then set INFO_PATH
using that temporary variableUpvotes: -1
Reputation: 645
For people getting here, reading things..
The problem seems to be that the regular expression (regex) in
location ~* ^/~(.+?)(/.*\.php)$ {
will never match a uri that does not end on .php, so the other regex will never "catch" anything in the last capturing group.
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
So changing that first regex to something like the following will "fix" that.
location ~ [^/]\.php(/|$) {
Upvotes: 1
Reputation: 71
I stumbled across a solution to this. The $fastcgi_path_info
var works together with $fastcgi_split_path_info
, and needs to be set within the location block. Here's what worked in our environment:
location ~ [^/]\.php(/|$) {
root /var/www/jurism-php;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
# Mitigate https://httpoxy.org/ vulnerabilities
fastcgi_param HTTP_PROXY "";
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
}
There is also an example in the Nginx documentation under fastcgi_split_path_info
.
(... which I now see matches more than one post above. Possibly the PATH_INFO line needs to be set after the include statement, to avoid clobbering the value.)
Upvotes: 7
Reputation: 1937
My working configuration is as follows:
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+\.php)($|/.*);
try_files $fastcgi_script_name =404;
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_ignore_client_abort off;
}
Upvotes: 5
Reputation: 7
here is what i got. and it works like a charm.
https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/
Upvotes: -1
Reputation: 1501
Late answer but it might be useful to someone.
I used the variable REQUEST_URI instead of PATH_INFO. Looks like it contains the same value as PATH_INFO is supposed to have.
Upvotes: 0
Reputation: 18250
Try this:
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
http://wiki.nginx.org/PHPFcgiExample
http://trac.nginx.org/nginx/ticket/321
Upvotes: 1