Nginx – Why Location Rule is Not Taking Precedence

nginx

I have a PHP application (WordPress) in the root folder of my site, and all requests to the site run through it happily.

However, I have a subfolder, _links, which I would like to use to execute another, different php script.

Eg, if a user goes to site.com/_links/221312, then the index.php script from /_links takes over.

I formerly used a .htaccess file to achieve this, but since moving to nginx I can't make it work.

Here is the old .htaccess file.

RewriteEngine On
RewriteRule    ^([0-9]+)/?$    index.php?linkid=$1    [NC,L]

Obviously, I'm passing the last bit of the URL to the script as a parameter.

Here is my nginx config. Requests to eg site.com/_links/23423 are picked up by if (!-e $request_filename) and rewritten to /index.php?q=/_links/23423 instead of being picked up by the location block.

server {
        listen  127.0.0.1:8080;
        server_name site.com www.site.com m.site.com;

        root /var/www/site;
        location ^~ /_links/ {
                add_header location 1;
                root /var/www/site/_links;
                rewrite "^([0-9]+)/?$" /index.php?linkid=$1 break;
                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;

                }

        }
        index index.php index.html;
        if (!-e $request_filename)
            {
                rewrite ^(.+)$ /index.php?q=$1 last;
            }
        location / {
                try_files $uri $uri/ =404;
        }

        # 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;

        }
        access_log /var/log/nginx/site.com.access.log;
        error_log /var/log/nginx/site.com.error.log notice;
}

I'm really confused. What's going on here?

Best Answer

Maybe because of the prefix match (^~) on the location block and nginx is skipping the regex check:

If the longest matching prefix location has the “^~” modifier then regular expressions are not checked

Try removing the prefix check i.e:

location /_links/ { 
Regex here 
}

Also remove the root declaration inside the location /_links/ block you don't need it, see nginx pitfalls for more information, basically servers root declaration is used then location is matched $root/$location

Also as suggested above have a look at the try_files directive instead of the if(condition){match}

I.e. instead of:

if (!-f $request_filename) {
    rewrite ^/(.*)$ /index.php?q=$1 last;
}

A better solution would be:

try_files $uri $uri/ /index.php?q=$uri;

This is also mentioned in the nginx pitfalls wiki page.