I’m building a site running NginX / PHP and want all access to be processed by /private/routes.php. But I also want to add some exceptions to this for css/js-files and the odd php file in the public directory (and sub dirs).
Folder / file structure:
/var/www/domain.com /private/routes.php /public/test.php /public/css/main.css /public/js/main.js
My current NginX config:
server { listen 80; listen [::]:80; server_name domain.com; root /var/www/domain.com/public; #index index.php; #allow existing css files location /css/ { try_files $uri =404; } #allow my defined php files location /test.php { try_files $uri =404; } #route everything else to my catch-all file location / { root /var/www/domain.com; try_files /private/nada.php /private/routes.php; #must add php here because of redefined root location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } } #handle allowed php files location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } }
The above config works but has some problems:
- I need to redefine root to run the catch all file (/private/routes.php).
- Because of #1 I need to add .php inside the location, so I end up with 2 php-location-configs.
- For some reason it doesn’t work if I remove “/private/nada.php” from the “try_files”.
- None-existing .php-files in /public returns 404 instead of being caught by the catch-all.
I could set the root to “/var/www/domain” but it seems a bit unnecessary since only the catch-all will use something else.
Is there any good ways to clean this up a bit?
Advertisement
Answer
Your configuration is really a mess. I’m not going to explain every mistake, otherwise it will make the answer 10 times longer. It took some time for me to figure out how it can be partially workable at all. The funniest thing here is that your location /test.php { ... }
isn’t really used to handle the /test.php
request (or you’d got the test.php
source code in response), however if you remove it, that request will be handled by the nested PHP handler instead of the root one.
Since the configuration you want to achieve does not fall into the usual use cases, you should not use that fastcgi-php.conf
which is some kind of pre-written PHP-FPM handler usable in most situations. It isn’t a part of the default nginx package provided by nginx team but is packaged with nginx by some distros, especially Debian-based ones. Since it is already includes the try_files
directive, and we need to use custom try_files
instead, we can’t use that file or nginx will complain for duplicate try_files
usage.
Here is a configuration that should work:
server { ... root /var/www/domain.com/public; # process an existing non-PHP file as a static one, fallback otherwise location / { try_files $uri @fallback; } # process an existing PHP file through PHP-FPM, fallback otherwise location ~ .php$ { try_files $uri @fallback; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$uri; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } # fallback to /private/routes.php location @fallback { include fastcgi_params; # we don't need a "root" directive here # instead we can specify the fallback filename directly fastcgi_param SCRIPT_FILENAME /var/www/domain.com/private/routes.php; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } }
This type of PHP-FPM usage is slightly different from the usual one since there are several FastCGI variables defined in the fastcgi_params
file that depends on actual root
used by nginx or actual $uri
variable value:
fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root;
I’ve never seen any PHP script somehow relying on those, the common practice is to rely on REQUEST_URI
variable value, but I think it worth to notice this difference. However these variables can be redefined, if needed (in that case this should be done after the include fastcgi_params;
line). For example, the following will emulate the usual behavior of the PHP handler completely:
location @fallback { include fastcgi_params; fastcgi_param SCRIPT_NAME /private/routes.php; fastcgi_param DOCUMENT_URI /private/routes.php; fastcgi_param DOCUMENT_ROOT /var/www/domain.com; fastcgi_param SCRIPT_FILENAME /var/www/domain.com/private/routes.php; fastcgi_pass unix:/run/php/php8.1-fpm.sock; }
or you can define a different root, rewrite an URI and use the commonly used one:
location @fallback { root /var/www/domain.com; rewrite ^ /private/routes.php break; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$uri; fastcgi_pass unix:/run/php/php8.1-fpm.sock; }
However, as being already said, most likely it isn’t required.
If under the public
directory you have subdirectories containing index.php
files and you want those files to be available without specifying the full path with the filename (e.g. domain.com/subdir/
rather than domain.com/subdir/index.php
), change the root location to this one:
location / { index index.php; try_files $uri $uri/ @fallback; }