Akarun Akarun - 1 month ago 22
PHP Question

Nginx - location with root in other directory and PHP

I tried to set a location similar as "Apache Alias" with Nginx but I don't able to process PHP script in this folder.

Here is my folder structure (for Dev environment):

/var/www
+- dev/public/ <-- This is my normal Web root : "/"
| +- assets/
| | +- app.css
| | +- app.js
| |
| +- index.php
| +- favicon.png
|
+- cut/public/ <-- This must like an "Apache Alias" : "/cut"
+- assets/
| +- app.css
| +- app.js
|
+- index.php
+- other_other_file.php (why not)


I've tried different solutions but none of them are working.

Here is my best Nginx configuration :

server {
listen 80;

server_name _;
root /var/www/dev/public/;
index index.php index.html;
autoindex on;

# Logs
rewrite_log on;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;

# Serving files
location / {
try_files $uri $uri/ @php;
}

location /cut {
root /var/www/cut/public;
try_files $uri $uri/ @php;
}

# PHP
location @php {
rewrite ^(.*)/?$ /index.php$is_args$args last;
}

location ~* \.php(/|$) {
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}


With this one, all the content of my
cut/public/
folder is redirected to the
dev/public/index.php
and interpreted (cause of
try_file
, I presume).

That is why your help would be welcome.

Final Solution

After the answer of @richard-smith, here's the implemented solution :

server {
listen 80;

server_name _;
root /var/www/dev/public/;
index index.php index.html;

error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;

location ^~ /cut {
rewrite ^/cut/?(.*)$ /cut/public/$1 last;
}

location ^~ /cut/public {
root /var/www/;
try_files $uri $uri/ /cut/index.php$is_args$args;

location ~* \.php(/|$) {
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

location / {
try_files $uri $uri/ /index.php$is_args$args;
}

location ~* \.php(/|$) {
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

Answer

Running two PHP applications side-by-side, you either need a common document root, or you need two location ~* \.php (or similar) blocks to ensure the correct SCRIPT_FILENAME is sent to the fastcgi backend.

Use nested location blocks to isolate the /cut subdirectory, and use the ^~ modifier at the top level to avoid other top level regular expression location blocks from interfering (see this documentation).

The alias directive (see this documentation) is used to map /cut to /var/www/cut/public. The root directive can only concatenate, which would make /var/www/cut/public/cut (which you do not want).

However, I would not recommend using the alias directive with the try_files directive because of this long term issue.

So, a solution would be to silently rewrite /cut to /cut/public and use a value of root /var/www.

For example:

location ^~ /cut {
    rewrite ^/cut(.*)$ /cut/public$1 last;
}
location ^~ /cut/public {
    root /var/www;
    try_files $uri $uri/ /cut/index.php$is_args$args;

    location ~* \.php(/|$) {
        fastcgi_pass  php:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}