Try_files ignored in combination with error_page and named locations

Hello,

For some reason the following nginx configuration snippet doesn’t work
as expected, try_files seems to be ignored. When / is requested (or any
other URI which points to a file which doesn’t exist), nginx’s default
503 page is returned instead of the existing index.html. Requesting
/index.html works correctly.

location @maintenance {
root /var/nginx/$hostid-maintenance;
index index.html;
try_files $uri $uri/ / =500;
}

error_page 503 @maintenance;

if ( -d $document_root/$hostid-maintenance ) {
return 503;
}

Any thoughts? Running nginx version 1.0.12 here.

Kind regards


Džen

On Tue, Apr 03, 2012 at 10:30:39PM +0200, Džen wrote:

}

error_page 503 @maintenance;

if ( -d $document_root/$hostid-maintenance ) {
return 503;
}

Any thoughts? Running nginx version 1.0.12 here.

First, this is a wacky config, and I don’t quite understand what
you’re trying to achieve with it. :slight_smile:

Here’s what happens with the request of “/”, assuming that “if”
condition holds true.

The status code is set to 503, and further processing happends in
the named location @maintenance.

It tries “$uri”, find that directory, but because argument does
not end with a slash (i.e., we’re testing a file, not directory),
this variant is ignored.

It then tries “$uri/”, and succeeds. $uri is set to
“/$hostid-maintenance/”. As documentation of “try_files” says,
the further processing is done in thi same (named) location.

What happens next is since a request now ends with a slash, the
static module handler ignores it, and a request is processed by
the index module. The index module then verifies that the
“/var/nginx/$hostid-maintenance/index.html” file exists and does
an internal redirect to a new $uri. (The fact that it does an
internal redirect is also documented.)

This in turn gets processed by the server’s default location
which is configured to return 503. Because
“recursive_error_pages” aren’t enabled, the server ends up
returning status code 503 with the standard content.

(What happens when you enable is left as an excercise to a
reader.)

When OTOH you request “/index.html”, try_files checks that the
file exists, and the processing is performed by the static
module in the context of location @maintenance. There are no
more internal redirects involved, so the server ends up sending
you the status code 503 with the contents of index.html.

I agree that this might not be obvious, but this is how it works.

The important bit here is that requests that end up with “/”, and
get processed by the index module, do an internal redirect. This
is quite logical if you think about it, because they usually
get processed by the static module, as if a request ended up with
“/” followed by the name of index file.

Thank you very much for your clarifying explanation. Seems like the only
thing missing was “recursive_error_pages on” for it to work like I
wanted it to have.

On 04/04/12 12:16pm, Ruslan E. wrote:

[…]

First, this is a wacky config, and I don’t quite understand what
you’re trying to achieve with it. :slight_smile:

Just to make you understand what I was trying to achieve with this
config: my maintenance page contains img tags pointing to images located
in $document_root. I used to add a simple

try_files /$hostid-maintenance/ @backend;

to my configuration, but as expected a request to /foo.png returns the
contents of /$hostid-maintenance/index.html instead. Thus I was looking
for a solution to use a whole directory as an error page (being able to
request other files which are located inside the maintenance
$document_root).

Here’s what happens with the request of “/”, assuming that “if”
condition holds true.

The status code is set to 503, and further processing happends in
the named location @maintenance.

It tries “$uri”, find that directory, but because argument does
not end with a slash (i.e., we’re testing a file, not directory),
this variant is ignored.

That’s OK in my case, since the maintenance “environment” might contain
other directories.

It then tries “$uri/”, and succeeds. $uri is set to
“/$hostid-maintenance/”. As documentation of “try_files” says,
the further processing is done in thi same (named) location.

I’m not really sure whether $uri is set to “/$hostid-maintenance/” if
“$uri/” is tried (since root is set to /var/nginx/$hostid-maintenance/
already). That works as expected and a request to / will actually just
try / relative to $document_root.

returning status code 503 with the standard content.
Thanks, that’s what I forgot about.

The important bit here is that requests that end up with “/”, and
get processed by the index module, do an internal redirect. This
is quite logical if you think about it, because they usually
get processed by the static module, as if a request ended up with
“/” followed by the name of index file.

Thanks again and kind regards


Džen

2012/4/4 Džen [email protected]:

Just to make you understand what I was trying to achieve with this

location /maintenance-static/ {

}

location / {
try_files /$hostid-maintenance/ @backend;
}

On 04/04/2012 11:03, Edho A. wrote:
[…]

location /maintenance-static/ {

}

location / {
try_files /$hostid-maintenance/ @backend;
}

Right, this would work just fine. However, requests to
/maintenance-static/
would still be possible even if /$hostid-maintenance/ doesn’t exist
(minor
problem, but maybe the backend uses /maintenance-static/ for some
reason).

Unfortunately, I still haven’t found a solution which pleases me. Just
to
clarify, this is how I want the request to be processed (el-cheapo
pseudo
code):

if the directory /maintenance/ exists, then
set root to /maintenance/
if the file $uri exists, then
serve $uri with errcode 503
otherwise
serve index.html with errcode 503
otherwise
proxy_pass http://backend

My new attempt:

recursive_error_pages on;
error_page 599 @maintenance;

if ( -d $document_root/$hostid-maintenance ) {
return 599;
}

location @maintenance {
root /var/nginx/$hostid-maintenance;
index index.html;
try_files $uri $uri/ /index.html /;
}

I used error code 599 because I noticed that for some reason the backend
might
raise an 503 error although $document_root/$hostid-maintenance doesn’t
exist.
The result in such a situation would be unexpected (an infinite redirect
loop?).
By using an unused error code like 599, nginx’s default 503 error page
will be
displayed instead, if the backend raises 503.

This configuration seems to work, but the 599 error code in the HTTP
header
isn’t that nice. Unfortunately, something like

error_page 599 =503 @maintenance;

doesn’t result in what I expected (it will again just return nginx’s
default
503 error page, although error 503 is correctly set in the HTTP header).

I still think that I misunderstood something, am I right?

Kind regards


Džen