38

I'm trying to serve request to /blog subdirectory of a site with the php code, located in a folder outside document root directory. Here's my host config:

server {
    server_name  local.test.ru;
    root   /home/alex/www/test2;

    location /blog {
        alias   /home/alex/www/test1;
        try_files $uri $uri/ /index.php$is_args$args;

        location ~ \.php$ {
            fastcgi_split_path_info ^(/blog)(/.*)$;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            include fastcgi_params;
        }
    }
}

And I get for requests like

wget -O - http://local.test.ru/blog/nonExisting

just a code of index.php file from /home/alex/www/test2/ folder.

However, this config:

server {
    server_name  local.test.ru;
    root   /home/alex/www/test2;

    location /blog {
        alias   /home/alex/www/test1;
        try_files $uri $uri/ /blog$is_args$args;
        index index.php;

        location ~ \.php$ {
            fastcgi_split_path_info ^(/blog)(/.*)$;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            include fastcgi_params;
        }
    }
}

gives me index.html file from /home/alex/www/test2/. Please give me a clue - why? And how can I force NGINX to process /home/alex/www/test1/index.php instead?

Alex Kovytin
  • 513
  • 1
  • 4
  • 8

3 Answers3

40

We could not get it to work by specifying root within the location block. The solution for us was to use alias instead. Note that it is necessary repeat the location's path twice in the try_files directive, and then also in the .php configuration block:

server {
    server_name localhost;
    root /app/frontend/www;

    location /backend/ {

        alias /app/backend/www/;

        # serve static files direct + allow friendly urls
        # Note: The seemingly weird syntax is due to a long-standing bug in nginx: https://trac.nginx.org/nginx/ticket/97
        try_files $uri $uri/ /backend//backend/index.php?$args;

        location ~ /backend/.+\.php$ {
            include fastcgi_params;
            fastcgi_buffers 256 4k;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            fastcgi_param HTTPS $proxied_https;
            fastcgi_pass phpfiles;
        }

    } # / location

}

Source: nginx/conf.d/app.conf from the debian-php-nginx stack in the docker-stack project

Motin
  • 4,853
  • 4
  • 44
  • 51
  • 1
    This worked for me putting a laravel install on /api location – fiw Jun 08 '16 at 20:43
  • 6
    "Note that it is necessary repeat the location's path twice in the try_files directive" .....?! It looks so wrong but actually works! Thanks, would have never figured that out! – scp Jul 22 '16 at 02:13
  • 3
    I COULD KISS YOU NOW – IanDess Oct 02 '16 at 19:02
  • 3
    Thanks man, I was gonna kill someone until i see your post. This bug has taken me almost the whole afternoon to find a solution. – Hammer Jan 06 '17 at 10:09
  • 1
    What's that double slash for ? `/backend//backend/` – dylanh724 Oct 13 '17 at 05:57
  • 3
    @DylanHunt There is a bug in nginx that makes it required to repeat the location directive twice here. Why? No idea – Motin Oct 17 '17 at 23:21
  • 1
    It also works foe me. About the double slash it seems to be related to having a trailing slash at the `location` block. – TCB13 May 09 '23 at 13:12
26

This is a long standing bug in nginx. But you can work around by using the root directive again. Kind of a hack, but at least it works.

server {
    index       index.php;
    root        /home/alex/www/test2;
    server_name local.test.ru;

    location /blog {
        root      /home/alex/www/test1;
        try_files $uri $uri/ /blog$is_args$args;
    }
}
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Fleshgrinder
  • 15,703
  • 4
  • 47
  • 56
19

There is another workaround which gives more flexibility. It consists of a proxy_pass directive which points on 127.0.0.1 and another server block.

In your case it should looks like this:

upstream blog.fake {
    server 127.0.0.1;
}
server {
    server_name local.test.ru;
    root /home/alex/www/test2;
    index index.html;

    location /blog {
        proxy_pass http://blog.fake/;
    }
}
server {
    server_name blog.fake;
    root /home/alex/www/test1;

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

    location ~ \.php(/|$) {
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS off;
    }
}
Melwin Kieffer
  • 380
  • 4
  • 11
  • 2
    elegant solution – kaklon May 09 '19 at 08:00
  • 3
    This is by far the best option! – 5413668060 Jun 28 '19 at 04:34
  • 2
    I really like this solution, makes it really easy understand what is happening and doesnt require complex nesting. – Chase Sep 11 '19 at 01:15
  • 2
    This deserves so much more recognition – Jazerix Feb 09 '20 at 00:26
  • 1
    This is the only solution that worked for me with: NGINX 1.14.0, PHP 7.4, Laravel 8.2. (The other solutions listed here didn't work.) – Joe C Nov 13 '20 at 22:15
  • I've implemented this fix but I experience an issue with the first URL, can see an example here in the 'Attempted fix': https://stackoverflow.com/questions/65093486/nginx-alias-breaks-due-to-try-files-uri-alias-bug – Dan Dec 02 '20 at 14:57
  • 3
    This solution is great, but it also needs to re-add the `/blog` part to the `REQUEST_URI` with `fastcgi_param REQUEST_URI /blog$request_uri;` to be completely transparent to PHP. – Slam Apr 15 '21 at 10:55