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, function ngx_file_info_wrapper. (called from ngx_open_cached_file, from ngx_http_script_file_code).

If disable_symlinks is set to off, nginx does a simple stat call (ngx_file_info is typedef'd to stat on *nix). When disable_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 a fstat() 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 then fstatat() 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:

On systems that do not support opening directories for search only, the use of these parameters requires that worker processes have read permissions for all checked directories.

I have uploaded a patch that allows you to restrict symlinks and still make try_files or if work. See also the linked thread above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.