I have several PHP sites which I want to separate in terms of permissions. Therefore I created a new user and set up a PHP pool for those users. Consider the below configuration:
- PHP-FPM is running as user
php
- nginx is running as user
nginx
- The webroot is
/srv/http
, accessible by both php and nginx. - The file
/srv/http/index.php
is readable by php, but not readable by nginx. - The file
fastcgi_stuff
contains basic fastcgi params for setting headers, setting the backend, etc. Assume that the webroot for static files and PHP files are the same.
The below nginx.conf contains the essentials of this configuration, it is placed in a server
directive:
server_name example.com;
root /srv/http;
index index.php;
location ~ \.php$ {
include fastcgi_stuff;
}
If I request http://example.com/
with the above configuration, the page is loaded as expected. For http://example.com/does.not.exist
, I get a 404 as expected.
To prevent passing inexistent files to PHP-FPM, I tried adding one of the below lines:
try_files $uri =404;
if ( !-e $request_filename ){ return 404; }
Both do not work as advertised, it tries to open the files instead of checking for existence by doing stat()
call. I confirmed this by checking the source code and running strace
on a worker process.
This is one basic case I was having trouble with, another case is where I hide the extension and therefore put a try_files $uri.php =404
in the location block. Any suggestions?
Nginx tries to open the file because I had
disable_symlinks on;
. The relevant source code is in core/ngx_open_file_cache.c, functionngx_file_info_wrapper
. (called fromngx_open_cached_file
, fromngx_http_script_file_code
).If
disable_symlinks
is set tooff
, nginx does a simple stat call (ngx_file_info
is typedef'd tostat
on *nix). Whendisable_symlinks
is set to something else (e.g.on
), it tries to open the file in a way that is safe against symlink races and then performs afstat()
call on the opened file descriptor. Since nginx cannot open the file for reading as it has no permissions for that, it fails.The short-term solution is to enable symlinks again using
disable_symlinks off
, a long term solution is to modify the functions to traverse to the containing directory and thenfstatat()
the file in that directory. Remember to disable symlinks for directories that do not contain PHP scripts, but are writable by the PHP user.As mentioned on http://forum.nginx.org/read.php?2,225152,234724#msg-234724, this behaviour is documented:
I have uploaded a patch that allows you to restrict symlinks and still make
try_files
orif
work. See also the linked thread above.