Opened 13 years ago

Last modified 18 months ago

#86 accepted defect

the "if" directive have problems in location context

Reported by: s "hr" berder Owned by: somebody
Priority: minor Milestone:
Component: nginx-core Version:
Keywords: Cc:
uname -a: Linux bjs-fl-dev-web06 2.6.18-274.12.1.el5xen #1 SMP Tue Nov 29 14:18:21 EST 2011 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/0.8.55
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-51)
TLS SNI support disabled
configure arguments: --user=nginx --group=nginx --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/subsys/nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module --with-http_perl_module --with-mail --with-file-aio --with-mail_ssl_module --with-ipv6 --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'

Description

To start, I'm doing tricky stuff so please don't point out at the weird things and stay focused on the issue at hand.
I'm mixing a configuration with userdir and symfony2 (http://wiki.nginx.org/Symfony) for a development environment, php is using php-fpm and a unix socket.
The userdir configuration is classic, all your files in ~user/public_html/ will be accessible through http://server/~user/.
I add to this the fact that if you create a folder ~user/public_html/symfony/ and put a symfony project in it (~user/public_html/symfony/project/) it will have the usual symfony configuration applied (rewrites and fastcgi path split).

Here you go for the configuration :

    # match 1:username, 2:project name, 3:the rest
    location ~ ^/~(.+?)/symfony/(.+?)/(.+)$ {
        alias /home/$1/public_html/symfony/$2/web/$3;
        if (-f $request_filename) {
            break;
        }
        # if no app.php or app_dev.php, redirect to app.php (prod)
        rewrite ^/~(.+?)/symfony(/.+?)/(.+)$ /~$1/symfony/$2/app.php/$3 last;
    }

    # match 1:username, 2:project name, 3:env (prod/dev), 4:trailing ('/' or
    # end)
    location ~ ^/~(.+?)/symfony(/.+)/(app|app_dev)\.php(/|$) {
        root /home/$1/public_html/symfony$2/web;
        # fake $request_filename
        set $req_filename /home/$1/public_html/symfony$2/web/$3.php;
        include fastcgi_params;
        fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_param SCRIPT_FILENAME $req_filename;
        fastcgi_pass unix:/tmp/php-fpm.sock;
    }

The second block (PHP backend) works on its own. The first block (files direct access) works on its own.

You can see that I already had a problem with PHP but went around it with creating my own variable.

To help understand, here is a sample of a symfony project layout (I removed some folders to help the comprehension):

project/
    src/
        [... my php code ...]
    web/
        app_dev.php
        app.php
        favicon.ico

If I try to access http://server/~user/symfony/project/favicon.ico I see this in the logs :

2012/01/17 16:36:25 [error] 27736#0: *1 open() "/home/user/public_html/symfony/project/web/favicon.icoavicon.ico" failed (2: No such file or directory), client: 10.11.60.36, server: server, request: "HEAD /~user/symfony/project/favicon.ico HTTP/1.1", host: "server"

If I remove the block that tests $request_filename, it works but I have to remove the rewrite as well.

The server is a CentOS 5.7 and the nginx is coming from the EPEL repository.

Unfortunately my C skills are down the floor so I can't really provide a better understanding of the problem. I tried to poke around the code but with not much luck.

Change History (9)

comment:1 by Maxim Dounin, 13 years ago

Status: newaccepted
Summary: alias and $request_filename don't play alongthe "if" directive have problems in location context
Version: 0.8.x

This is one of the known problems with the "if" directive used in a location context, see http://wiki.nginx.org/IfIsEvil. Reduced test case is as follows (copied from the wiki page in question):

        # alias with captures isn't correcly inherited into implicit nested
        # location created by if
 
        location ~* ^/if-and-alias/(?<file>.*) {
            alias /tmp/$file;
 
            set $true 1;
 
            if ($true) {
                # nothing
            }
        }

Workaround is not to use if, or use it in a safe way, see the wiki page in question.

comment:2 by Maxim Dounin, 11 years ago

sensitive: 0

A particular case with regex location + alias + if is resolved by c985d90a8d1f.

Leaving the ticket open as there are other cases which still need to be fixed, see ​http://wiki.nginx.org/IfIsEvil.

comment:3 by Maxim Dounin <mdounin@…>, 10 years ago

In aeea0522332f4007b30683ad2a8c77a9a4bd2371/nginx:

Proxy: fixed incorrect URI change due to if (ticket #86).

In the following configuration request was sent to a backend without
URI changed to '/' due to if:

location /proxy-pass-uri {

proxy_pass http://127.0.0.1:8080/;

set $true 1;

if ($true) {

# nothing

}

}

Fix is to inherit conf->location from the location where proxy_pass was
configured, much like it's done with conf->vars.

comment:4 by Maxim Dounin, 8 years ago

See also #633, which is basically identical to the same problem with if:

# try_files wont work due to if

location /if-try-files {
     try_files  /file  @fallback;

     set $true 1;

     if ($true) {
         # nothing
     }
}

In both cases try_files is not inherited into an implicit location created with if / limit_except and this results in unexpected behaviour.

comment:5 by Maxim Dounin, 7 years ago

See also #1383.

comment:6 by Maxim Dounin, 7 years ago

See also #1434.

comment:7 by Maxim Dounin, 7 years ago

See also #1507.

comment:8 by Maxim Dounin, 6 years ago

See also #1707.

comment:9 by Maxim Dounin, 18 months ago

See also #2511.

Note: See TracTickets for help on using tickets.