Try_files and a nested location regexp

I could not figure out why try_files in a nested location defined with a
regexp does not work in nginx/1.4.6 under Ubuntu 14.04. Consider the
following config:

server {
listen 8080 default_server;
root /usr/share/nginx/html;
autoindex on;

    location /x/ {
            alias /test/;
            location ~ ^/x/test {
                    try_files $uri =404;
            }
    }

}

With /test containing publically readable file test.html and directory
test_dir this does not works as expected. While localhost:/x/ properly
lists directory context of /test and localhost:/x/test_dir/ is reported
as
404 not found , nginx also reported as 404 not found
localhost:/x/test.html
even if the file exists.

Now, if replace the regexp with a simple prefix so the location reads:

    location /x/ {
            alias /test/;
            location /x/test {
                    try_files $uri =404;
            }
    }

then everything work. That is, both localhost:/x/ and
localhost:/x/test.html
are accessible and only localhost:/x/test_dir/ is 404 not found.

So what is wrong with the usage of try_files in the initial regexp-based
location config?

Posted at Nginx Forum:

On Oct 16, 2014, at 1:38 PM, igorb [email protected] wrote:

[…]
So what is wrong with the usage of try_files in the initial regexp-based
location config?

That is because a location defined with a regular expression has no
fixed
length to make a replacement in try_files, which is what alias do.


Sergey Kandaurov

I tried to add explicit alias to the regexp location:

server {
listen 8080 default_server;
root /usr/share/nginx/html;
autoindex on;

    location /x/ {
            alias /test/;
    }

    location ~ ^/x/(test.*)$ {
            alias /test/$1;
            try_files $uri =404;
    }

}

However that still gives 404 for localhost/x/test.html . Does that mean
that
try_files cannot be used at all in a regexp location defined with an
alias,
it only works if the location uses the root directive?

Posted at Nginx Forum:

I tried that, but it still does not work. The following config as before
still gives 404 for localhost/x/test.html :

server {
listen 8080 default_server;
root /usr/share/nginx/html;
autoindex on;

    location /x/ {
            alias /test/;
    }

    location ~ ^/x/(test.*)$ {
            alias /test/$1;
            try_files /test/$1 =404;
    #       try_files $uri =404;
    }

}

Posted at Nginx Forum:

On Thu, Oct 16, 2014 at 07:09:44AM -0400, igorb wrote:

Hi there,

    location ~ ^/x/(test.*)$ {
            alias /test/$1;
            try_files $uri =404;
    }

However that still gives 404 for localhost/x/test.html . Does that mean that
try_files cannot be used at all in a regexp location defined with an alias,
it only works if the location uses the root directive?

try_files puts its “file” argument after $document_root, and looks for
a file on the filesystem with that combined name.

alias-with-regex sets $document_root to the value given.

So if you want try_files and alias-with-regex, you want something like

try_files “” =404

But that specific use of try_files is probably not very useful, since
it is mostly the same as having no try_files at all – I guess it is a
simplified version of what you really want, so it is useful as an
example.

f

Francis D. [email protected]

On Thu, Oct 16, 2014 at 8:09 PM, igorb [email protected] wrote:

    location ~ ^/x/(test.*)$ {
            alias /test/$1;
            try_files $uri =404;
    }

}

However that still gives 404 for localhost/x/test.html . Does that mean that
try_files cannot be used at all in a regexp location defined with an alias,
it only works if the location uses the root directive?

Why don’t just use the alias in try_files?

try_files /test/$1 =404;

Thanks, try_files “” =404 works indeed as long as the regexp location
block
contains the necessary alias. I.e. the original example modified like
in:

server {
listen 8080 default_server;
root /usr/share/nginx/html;
autoindex on;

location /x/ {
alias /test/;
location ~ ^/x/test {
try_files “” =404;
}
}
}

does not work, while the following one with an extra alias works nicely:

server {
listen 8080 default_server;
root /usr/share/nginx/html;
autoindex on;

location /x/ {
alias /test/;
location ~ ^/x/(test.*) {
alias /test/$1;
try_files “” =404;
}
}
}

P.S.

This is indeed a very simplified config. However, even in this form it
is
useful as it reports directories matching ^/x/test as not found even
with
autoindex on inherited from the server context.

Posted at Nginx Forum:

That does not work either. What works is try_files “” =404 together with
an
explicit alias as Francis D. described in another post.

Posted at Nginx Forum:

On Thu, Oct 16, 2014 at 8:25 PM, igorb [email protected] wrote:

    }

    location ~ ^/x/(test.*)$ {
            alias /test/$1;
            try_files /test/$1 =404;
    #       try_files $uri =404;
    }

}

Have you tried removing the alias?

location ~ ^/x/(test.*)$ {
try_files /test/$1 =404;
}

On Thu, Oct 16, 2014 at 07:55:48AM -0400, igorb wrote:

Hi there,

Thanks, try_files “” =404 works indeed as long as the regexp location block
contains the necessary alias.

That sounds correct.

“alias” sets $document_root. try_files concatenates $document_root with
its “file” argument. (It does do more than that; those details might
matter depending on what precisely you want to do.)

So if you want to use try_files, you must make sure that $document_root
and the “file” argument combine to name the file that you want to check
the existence of.

location /x/ {
alias /test/;
location ~ ^/x/test {
try_files “” =404;
}
}

does not work,

$document_root concatenated with “” is /test/, which is not a file,
so =404 is used.

This is indeed a very simplified config. However, even in this form it is
useful as it reports directories matching ^/x/test as not found even with
autoindex on inherited from the server context.

If you want try_files to look for a directory instead of a file, you
have to configure it to do that.

f

Francis D. [email protected]

On Thu, Oct 16, 2014 at 9:03 PM, igorb [email protected] wrote:

That does not work either. What works is try_files “” =404 together with an
explicit alias as Francis D. described in another post.

did you put it inside the aliased location block? That’d explain why
it doesn’t work (as francis said, $document_root is overridden) unless
you add “root /test;” inside the block.

Seriously though, you better avoid using alias at all.

On Thu, Oct 16, 2014 at 08:42:26AM -0400, igorb wrote:

Hi there,

Thanks again for detailed explanation. Now I almost grasped how try_files
works. “Almost” because I still do not see why the following does not work:

There is a defect which is involved here, and probably interferes:

http://trac.nginx.org/nginx/ticket/97

Anything involving try_files and alias may not do what you expect.

If you can specify what exactly you want, it may be possible to find a
configuration which does that using the current implementation of the
defect. Or it may not be.

You will probably be much happier investigating if you enable the
debug log.

location /x/ {
alias /test/;
location ~ ^/x/(test.*) {
try_files $1 =404;
}
}

For localhost/x/test.html $1 will be test.html. I suppose $document_root
should be /test/ as it was set with the alias in the outer location so
try_files should try to check for existing /test/test.html . However, nginx
still reports 404. Why it is so?

Can you tell whether the 404 is from the uri argument of try_files,
or the serve-from-filesystem handler?

Look in the debug log and you will see what happens.

Or: what do you see if you change try_files to end in =405?

The rest is “the details of what else try_files and alias do”.

(try_files does see /test/test.html, but the serve-from-filesystem
handler does not try to serve that file.)

f

Francis D. [email protected]

Thanks again for detailed explanation. Now I almost grasped how
try_files
works. “Almost” because I still do not see why the following does not
work:

server {
listen 8080 default_server;
root /usr/share/nginx/html;
autoindex on;

location /x/ {
alias /test/;
location ~ ^/x/(test.*) {
try_files $1 =404;
}
}
}

For localhost/x/test.html $1 will be test.html. I suppose $document_root
should be /test/ as it was set with the alias in the outer location so
try_files should try to check for existing /test/test.html . However,
nginx
still reports 404. Why it is so?

Posted at Nginx Forum: