What is fastcgi_index in nginx used for? - nginx

On many sites can be found this nginx location block :
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000
fastcgi_index index.php
...
}
Given the official documentation of fastcgi_index, it seems like it is used when requests end with /. However, it doesn't match the regular expression of the location block above? Am I missing something about the fastcgi_index directive?

You are right, if your nginx configuration (outside the location directive) has no index directive, then the location directive will never match and the fastcgi_index directive is useless.
If you have a line like this on your configuration
index index.php
then a request to / will create an internal redirect to /index.php, the location will match and fastcgi will be called. php-fpm will need a SCRIPT_FILENAME parameter that points to the file being executed. Normally, the configuration looks something like this:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
$fastcgi_script_name contains the name of the matched script, so fastcgi_index is ignored.
There is at least one instance where fastcgi_index is useful and used: when nginx and php-fpm are on different servers and nginx can't match the index.php file.

Related

Converting Apache .htaccess to Nginx Directives

I've run my site on Apache for years, and since I've made the transition over to Nginx for speed preference, my htaccess rewrite rules have broken my site.
My site content is served dynamically based on the URL request - here's how my htaccess file looks for / traffic.
RewriteEngine on
RewriteRule ^([a-zA-Z0-9-z\-\_]+)/$ index.php?first_path=$1 [QSA]
This will pass any directory as a first_path param to index.php.
Reading through the Nginx documentation for url restructuring, I can't seem to figure out how to write my requirements.
Any ideas will help.
You may have some luck with online converters like this one, which specifically to your .htaccess will produce:
server {
server_name example.com;
rewrite ^/([a-zA-Z0-9-z\-\_]+)/$ /index.php?first_path=$1;
}
Naturally, you also have to include a "handler" location for PHP processing so a more complete configuration would be:
server {
server_name example.com;
rewrite ^/([a-zA-Z0-9-z\-\_]+)/$ /index.php?first_path=$1;
location ~ \.php$ {
fastcgi_pass unix:/path/to/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
It's worth noting though, that the creator of NGINX himself, Igor Sysoev, is not in favor of using rewrite directives, dismissing their use to old times when nested locations were not supported.
Nested locations, as per NGINX author allow isolating regular expression thus creating a configuration that is easier to maintain in the future (not necessarily easier to create at first though). So in this case, using nested location, the same config may look like this:
server {
server_name example.com;
location / {
location ~ ^/(?<dir>[a-zA-Z0-9-z\-\_]+)/$ {
fastcgi_pass unix:/path/to/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME /path/to/index.php;
fastcgi_param QUERY_STRING first_path=$dir
include fastcgi_params;
}
}
location ~ \.php$ {
fastcgi_pass unix:/path/to/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

Nginx load subpath as wordpress root

I'm trying to set up a Wordpress in a system that has another php application installed, using nginx as web server.
I've simplified my config file to the maximun. The following confi is serving one post of my blog:
server {
listen 80;
server_name blog.ct.com;
root /home/ff/www/blog/;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$uri&$args =405;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_buffer_size 128k;
fastcgi_buffers 64 32k;
fastcgi_busy_buffers_size 128k;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param APPLICATION_ENV development;
fastcgi_param HTTP_X_FORWARDED_PROTO https;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
}
}
But, due my system's requirements I need to serve the blog from within a sub path (In my final system http://blog.ct.com/ should be serving my custom php app and http://blog.ct.com/vendor should be serving the wordpress blog).
The local root directory from wordpress must be /home/ff/www/blog/ (this cannot be changed, while my custom app's directory is /home/ff/www/myapp/). So I think I need to reserve location / for my custom app, I have to create a location /vendor
If I add /vendor and I return 403 in / (just to debug easier), the browser says 405 (notice the =405 in /vendor, also to debug easier):
location /vendor {
try_files $uri $uri/ /index.php?$uri&$args =405;
}
location / {
return 403;
}
So I think nginx is going into location /vendor but is not finding my php script in /home/ff/www/blog/index.php so its returning the fallback 405.
Any idea why this could happen?
How can I achieve to load http://blog.ct.com/vendor as the root from wordpress but keeping http://blog.ct.com/ using another php script?
I've found out the following hints that gave me the clue to fix the problem (in case someone has the same problem than me, this may help)
Using location /path is not the same as using location ~(/path) (regex have different priority, so maybe they are not being checked in the order you think)
Adding error_log /your/path/log/error.log debug; to any location block may help you to see how is nginx serving every request (e.g. to location fastcgi, location \vendor, or the server{ block).
alias /var/www/path/vendor works different than root /var/www/path/vendor (check Nginx -- static file serving confusion with root & alias);
In case of the root directive, full path is appended to the root including the location part, whereas in case of the alias directive, only the portion of the path NOT including the location part is appended to the alias.
using rewrite with alias can help you parse the php file you want independent of the path
if (!-f $request_filename) {
rewrite ^ $document_root/index-wp.php last;
}
Take care of the SCRIPT_FILENAME you are using (check it with error_log, see above), maybe you need fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; but you are loading fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; so depending on your previous config you may be attaching the document root twice.
Two different configurations for fastcgi can be used if you change your index.php file names. E.g. location ~ wp\.php$ { will work with wp.php while location ~ \.php$ { will work with all other php files like index.php.

Nginx, fastcgi PHP Windows, No input file specified

Running php-cgi on port 9000
Netstat gives me
TCP 127.0.0.1:9000 DESKTOP-xxxxxxx:0 LISTENING
[php-cgi.exe]
nginx.conf
http://pastebin.com/wkfz8wxw
Every php file gives me this No input file specified. error...
Changed SCRIPT_FILENAME to SCRIPT_NAME and no succes..
I am on Windows 10 Home x64
You need to set a document root with the root directive, either within your location ~ \.php$ block or inherited from the outer server block.
The solution may be to move the root c:/Users/Youri/PhpstormProjects; line out of your location / block into a position above it.
Usually fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; is the correct method to specify the full path to the script, whereas SCRIPT_NAME is usually just the last element.
Like this:
server {
...
root c:/Users/Youri/PhpstormProjects;
location / {
index index.html index.htm index.php;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Set root directive with absolute path in windows PC.
Check for the case or any typing mistake in the path.
Reload the nginx process.
May be the root path problem. I used the backslash \ on Windows and found this issue.
server {
location ~ \.php$ {
- root C:\Users\name\ProjectOfPHP; # 404
+ root C:/Users/name/ProjectOfPHP; # Use this pattern
}
}

FPM/Nginx: unable to open primary script

I'm struggling to get NGINX and PHP-FPM to talk effectively. My NGINX configuration file includes the following definition for the api offset:
location /api {
try_files $uri /api/index.php$is_args$args;
gzip off;
fastcgi_pass PHP:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_keep_conn off;
include fastcgi_params;
}
and on my PHP machine I have the following Pool configuration:
[api]
listen = 9000
user = www-data
group = www-data
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.status_path = /status
I then tail the NGINX server's log file and when hitting the webserver with:
http://localhost/api
# or
http://localhost/api/index.php
# or ...
http://localhost/api/resources.json
I can see from the NGINX server log that NGINX is correctly matching the /api pattern but i get the following errors:
[error] 14#0: *1 FastCGI sent in stderr: "Unable to open primary script: /app/html/websites/couchbase/api/index.php (No such file or directory)"
Where the root directory on the PHP machine is /app/html/websites/couchbase. I'm at a complete loss on what this error really means or more importantly how to debug it from here. Any help would be greatly appreciated.
p.s. I have tried replacing $document_root reference in the fast_cgi_param to hard coded values to see if it makes a difference. It does in the sense that if I point it at an incorrect directory it gives me
a No input file specified. error. The only place where I get the error is when it seemingly is pointed to the right place.
Added this in case it helps clarify ... this is the file system at /app/html/websites/couchbase/api on the PHP/FPM machine.
I created the test.php file which basically just echo's "Ok" back but that works no better than the primary goal of running index.php.
note: I wasn't sure if execution permissions were important -- as you can see test.php does not have them set in the picture -- but I've tried them both ways and it appears to make no difference.
The error is complaining about the index.php file not found in the root directory. Is it under ...couchbase/api or is in under .../couchbase? In the second case you can fix this problem with a redirection.
I think you can move try_files outside the location block. I never used $is_args and $args in this kind of redirection, and I've not tested it but IMHO I think you can remove it.
I think you can fix this problem with this configuration:
root /app/html/websites/couchbase;
try_files $uri /api/index.php$is_args$args;
location /api {
rewrite /api/(.*) $1 break;
gzip off;
fastcgi_pass PHP:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_keep_conn off;
include fastcgi_params;
}
You can have a cleaner config file if you'll include the following directives:
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_keep_conn off;
inside the fastcgi_params file.

Nginx + phpFPM: PATH_INFO always empty

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?
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.)
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.
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;
}
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
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(/|$) {
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.
here is what i got. and it works like a charm.
nginx 1.10.1
php 5.6.24
https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/
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:
I installed 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;
using 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.
so what we do is save INFO_PATH to a temporary variable and then set INFO_PATH using that temporary variable

Resources