Have a server running nginx+php-fpm and all running fine except for inconsistent handling of 404s.
example.com/fakepath
example.com/fakepath.php
The first link passes through to our app which handles 404 page, logging etc.
The second link returns an error outside our app with a blank page showing just "File not found."
Is this being caused by the nginx php block, or the php-fpm side?
Any setting to get php files that don’t exist passed back to the index.php instead of the not found error?
location / {
index index.php index.html;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ .php$ {
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_split_path_info ^(.+.php)(/.+)$;
include fastcgi_params;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
2
Answers
This happens because priority of regex matching locations like
location ~ .php$ { ... }
is greater than prefix locations likelocation / { ... }
. An exact location selection algorithm decribed in thelocation
directve documentation:The above means than when you get a
/fakepath.php
request, it’s processing goes directly to thelocation ~ .php { ... }
where you didn’t have anytry_files
directive. Error you’ve got is coming from PHP-FPM daemon response when you pass a non-existent file path to it viaSCRIPT_FILENAME
FastCGI parameter. The most simple is to add anothertry_files
directive to the second location block:However due to
try_files
directive behavior described here you’ll be unable to setPATH_INFO
FastCGI parameter using your current config. I don’t think you really need it for you PHP web app since with your regexPATH_INFO
will be always empty anyway (and if you really do, change your regex to.php($|/)
like shown here and use a suggested workaround). The only modern software I know that is really usingPATH_INFO
nowadays is the Craft CMS, most of them (including most popular like WordPress, Laravel Framework, etc.) relies onREQUEST_URI
FastCGI parameter instead. Most probably you can safely remove thosefastcgi_split_path_info ^(.+.php)(/.+)$;
andfastcgi_param PATH_INFO $fastcgi_path_info;
lines from your config (as I already said, using.php$
regex with thelocation
directive those lines don’t do anything anyway).While the solution by @Ivan is great, performance-wise you’re adding another
try_files
in addition to the already existing one. This means that even for valid SEO-friendly URLs, e.g./fakepath
, NGINX will routinely check file/fakepath
, dir/fakepath
and file/index.php
for existence.You can achieve the desired effect (that is, get rid of "No input file specified" coming from PHP-FPM) without performance impact, like so:
fastcgi_intercept_errors on;
will enable NGINX to process theerror_page
directive specified in the location. That is, it will see 404 status from PHP-FPM and will handle the error using the specified static page.The
/404.html
may not exist at all, and then NGINX will deliver its internal 404 page (the desired effect).You may want to specify, e.g.
/404.php
or other framework-specific not-found/error handler that is dynamic. In that case, it’s best to cache it.