skip to Main Content

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


  1. This happens because priority of regex matching locations like location ~ .php$ { ... } is greater than prefix locations like location / { ... }. ​An exact location selection algorithm decribed in the location directve documentation:

    A location can either be defined by a prefix string, or by a regular expression. Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching). To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

    If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.

    Also, using the “=” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates. For example, if a “/” request happens frequently, defining “location = /” will speed up the processing of these requests, as search terminates right after the first comparison.

    The above means than when you get a /fakepath.php request, it’s processing goes directly to the location ~ .php { ... } where you didn’t have any try_files directive. Error you’ve got is coming from PHP-FPM daemon response when you pass a non-existent file path to it via SCRIPT_FILENAME FastCGI parameter. The most simple is to add another try_files directive to the second location block:

    location ~ .php$ {
        try_files $uri /index.php$is_args$args;
        ...
    }
    

    However due to try_files directive behavior described here you’ll be unable to set PATH_INFO FastCGI parameter using your current config. I don’t think you really need it for you PHP web app since with your regex PATH_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 using PATH_INFO nowadays is the Craft CMS, most of them (including most popular like WordPress, Laravel Framework, etc.) relies on REQUEST_URI FastCGI parameter instead. Most probably you can safely remove those fastcgi_split_path_info ^(.+.php)(/.+)$; and fastcgi_param PATH_INFO $fastcgi_path_info; lines from your config (as I already said, using .php$ regex with the location directive those lines don’t do anything anyway).

    Login or Signup to reply.
  2. 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:

    location ~ .php$ {
        fastcgi_intercept_errors on;
        error_page 404 /404.html;
        ...
    }
    

    fastcgi_intercept_errors on; will enable NGINX to process the error_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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search