Nginx – Changing Root Folder for Specific URL Gives 404 Error

nginxweb-hostingweb-server

I am trying to arrange a location block for anyone get through mydomain.com/game/admin url, make sure the nginx server to pull the content exists in /var/www/html/my-cakephp-app/ directory. My application is built using cakephp framework and its directory structure is shown below:

  • /var/www/html/my-cakephp-app/
    • admin
      • Config
      • Console
      • Controller
      • View
      • webroot (App entry-point index.php file exists in this directory)

Also I have static html/css website located in the /var/www/html directory. So anyone with mydomain.com url can see that website too.

Here is my current nginx server block:

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

    root /var/www/html;

    index index.html index.htm index.php;

    server_name mydomain.com;

    location / {
        try_files $uri $uri/ =404;
    }
    
    location /game/admin {
            return 301 /game/admin/;
    }

    location /game/admin/ {

                root /var/www/html/my-cakephp-app/admin/webroot;
                try_files $uri $uri/ /game/admin/index.php$is_args$args;
  
                location ~* \.php(/|$) {
                  include snippets/fastcgi-php.conf;
                  fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
                  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
               } 

    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

With this setup, my static web-site working fine. But cakephp application gives 404 not found error in browser. No any errors in the nginx/error.log.

But when I run with below nginx configuration, my application works fine. But I have to get rid of my html/css site.I am planning to upgrade html/css app with a wordpress site. So I should have the ability to run the wordpress site as the parent.

server {
    listen 80;
    server_name mydomain.com;
    root /var/www/html/my-cakephp-app/admin/webroot;
    
    index index.html index.htm index.php;
 
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
        autoindex on;
    }
    
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }
    
    location ~ /\.ht {
        deny all;
    }
}

I couldn't think of what I have done wrong with the first server block. Any suggestions would be really helpful.

Best Answer

The two main problems are:

  • the outer location ~ \.php$ block takes precedence over the location /game/admin/ block unless you use the ^~ modifier (see this document for details)
  • the root directive generates a path to the file by simple concatenation, so your controller is expected to be located at /var/www/html/my-cakephp-app/admin/webroot/game/admin/index.php (see this document for details)

One option is to move the project so that the directory structure matches the URI structure. This could be achieved using a symbolic link which points /var/www/html/game/admin to /var/www/html/my-cakephp-app/admin/webroot in which case the outer location ~ \.php$ block will be able to execute both projects.


Another option is the alias directive. See this document for details.

location ^~ /game/admin {
    alias /var/www/html/my-cakephp-app/admin/webroot;

    if (!-e $request_filename) { rewrite ^ /game/admin/index.php last; }

    location ~ \.php$ {
        if (!-f $request_filename) { return 404; }

        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $request_filename;
    }
}

Note that $document_root$fastcgi_script_name will not work with alias and that $request_filename should be used instead.

I avoid using alias and try_files together due to this issue. See this caution on the use of the if directive.