PHP in NGINX directory alias not working

I have a fresh install of Ubuntu 14.04 server. I have installed nginx, php, etc....

server {
  listen 80;
  listen [::]:80;

  server_name testone.local;

  root /var/www/htmlone;
  index index.html;

  # pass the PHP scripts to FastCGI server listening on the php-fpm socket
  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
  }

  location /alias {
    alias  /var/www/htmlalias;
    location ~ \.php$ {
      try_files $uri =404;
      fastcgi_pass unix:/var/run/php5-fpm.sock;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      include fastcgi_params;
    }
  }
}

If I use a simple php script in /var/www/htmlone php executes as expected. If I use the same script in /var/www/htmlalias it does not execute as expected. If I put an HTML script in /var/www/htmlalias it does however display as expected, so the alias acts as an alias, but does not execute php files, but php works in the primary root directory.

I have found on many serverfault questions that this general setup should work yet it is not. Does anyone see anything that I may be doing wrong? I see no messages in the error log.

I should add this is for nginx version: nginx/1.8.0


The issue you're having is actually a long-standing bug that was filed 3 years ago which causes alias and try_files to not really work together.

On the bug page there is a workaround by Luke Howell, which goes as follows:

location /api { ## URL string to use for api ##
    alias /home/api/site_files/; ## Site root for api code ##

    ## Check for file existing and if there, stop ##
    if (-f $request_filename) {
        break;
    }

    ## Check for file existing and if there, stop ##
    if (-d $request_filename) {
        break;
    }

    ## If we get here then there is no file or directory matching request_filename ##
    rewrite (.*) /api/index.php?$query_string;

    ## Normal php block for processing ##
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

Do note though that as far as nginx is concerned, IF is evil and should be avoided if possible.


From my testing of the above configuration it is the presence of

try_files $uri =404;

inside the nested alias php location that causes the problem. With it, nginx checks for "/var/www/htmlalias/alias/index.php" (note the addition of the 'alias' plus the uri), finds it doesn't exist, and then returns a 404. Removing that try_files stops it looking for this file on disk first, and passes the request directly to fastcgi, which then finds the correct file from SCRIPT_FILENAME.

If you want non-existent PHP files to give a 404 rather than a PHP error, then the following works:

  location /alias {
    alias  /var/www/htmlalias;
    location ~ /([^/]+\.php)$ {
      try_files /$1 =404;
      fastcgi_pass unix:/var/run/php5-fpm.sock;
      fastcgi_index index.php;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
  }

First the regex captures the fill filename of the php file (e.g. foo.php) as $1. try_files then checks, relative to the current alias, if that file exists, returning a 404 if it doesn't.

We then have to override the default SCRIPT_FILENAME as defined in fastcgi_params by redefining it after the include, because $request_filename will, for some reason I don't understand, be entirely the wrong thing (literally /index.php).