Try_files and location blocks

Hello. :slight_smile: Could someone please correct my understanding of location
blocks and try files? I’m trying to get http errors to be very graceful,
normally being handled by php but in extreme cases returning the proper
numeric error. For my example, just consider 404:

server {

try_files $uri $uri/ /error.php?c=404

 error_page 404 = @error_404;
 location @error_404 {
     try_files /error.php @not_found;
     rewrite ^ $scheme://$server_name/error.php?c=404
 }
 location @not_found {
     return 404;
 }

}

I’m not clear why this does not work. I rewrite the above example
as:

server {

location / {
try_files $uri $uri/ /error.php?c=404;
}

 location /error.php {
    try_files /error.php @not_found;
 }

 error_page 404 = @not_found;
 location @not_found {
     return 404;
 }

}

and it appears to work. I think the downside of this is that
I have to have the location /error.php explicitly invoke php.

While I enjoy hacking at something, I also enjoy understanding something
just as well. So what am I confused about?

Thanks in advance.

Dave H. - Consultant - Altadena CA, USA - [email protected]

The opinions expressed above are entirely my own <<<

“That’s a very dangerous lake, Nasrudin. People who swim in
it are always being found dead at the bottom.”

“That’s alright, friend”, replied Nasrudin. “I’ll keep well
away from the bottom…”

On 23 Jun 2011 22h37 WEST, [email protected] wrote:

Hello. :slight_smile: Could someone please correct my understanding of location
blocks and try files? I’m trying to get http errors to be very
graceful, normally being handled by php but in extreme cases
returning the proper numeric error. For my example, just consider
404:

My take on it.

return 404;
}
}

There are no “regular” locations, meaning, regex or others. Only
“special” named locations. They must be invoked explicitly in your
config.

NGX_HTTP_TRY_FILES_PHASE happens at server level. Tries to serve the
requested
URI either as a file or a dir. Fallbacks to error.php with arg c=404;

If the file is not found then signals a 404, error_page directive
redirects to @error_404 location.

@error_404 location runs rewrite phase directive, rewriting to
$scheme://$server_name/error.php?c=404

Try files phase runs again and serves the URI /error.php with arg c=404.

try_files /error.php @not_found;
}

error_page 404 = @not_found;
location @not_found {
return 404;
}
}

Now the processing will try to find a matching location. If none is
found than use the ‘/’ “catch-all” location.

Any URI != error.php will be served by handlers you have specified in
the ‘/’ location and those that are inherited from outside (the rule
with some exceptions).

In this case runs the try_files directive like above, fallbacks to
error.php with arg c=404.

Now enters /error.php location, runs try_files again, tries error.php
fallbacks to @not_found. Location @not_found runs rewrite phase
directive return 404. Status 404 is returned. No more processing here.

Note that the error_page directive is only invoked if you don’t have
the error.php file in place. Otherwise it will never run.

and it appears to work. I think the downside of this is that
I have to have the location /error.php explicitly invoke php.

While I enjoy hacking at something, I also enjoy understanding
something just as well. So what am I confused about?

HTH,
— appa

PS: You can verify this flow in your error log by using the debug flag
with the error_log directive.

On 23 Jun 2011 23h02 WEST, [email protected] wrote:

Note that the error_page directive is only invoked if you don’t have
the error.php file in place. Otherwise it will never run.

Oops. Should be:

Note that the error_page directive is only redirects to @not_found
if you don’t have the error.php file in place. Otherwise the
redirect to @not_found will never run.

— appa

On 23 Jun 2011 23h09 WEST, [email protected] wrote:

Also your later config could be simplified:

server {

location / {
try_files $uri $uri/ /error.php?c=404 =404;
}
}

— appa

Thanks very much for looking at this.

[email protected] writes:

Also your later config could be simplified:
server {

location / {
try_files $uri $uri/ /error.php?c=404 =404;
}
}

Where is this “=” notation documented? That try_files line looks exactly
like what I’m trying to do and appears much more elegant.

I was under some impression that try_files could not take arguments with
a URI, which may be an assumption based on the name of the directive.
It’s not really documented that try_files can do this either, unless
I’m looking in the wrong place?

Core functionality

Right?

Finally, exactly what happens if you have location blocks but none of
them match and you haven’t specified a default?

Dave H. - Consultant - Altadena CA, USA - [email protected]

The opinions expressed above are entirely my own <<<

It is only knowledge that will destroy bias.

On 23 Jun 2011 23h46 WEST, [email protected] wrote:

Модуль ngx_http_core_module

Use google translate :slight_smile: Yes it’s missing from the wiki :frowning:

It’s no longer missing. Feel free to improve it.

http://wiki.nginx.org/CoreModule#try_files

— appa

On 23 Jun 2011 23h29 WEST, [email protected] wrote:

Where is this “=” notation documented? That try_files line looks
exactly like what I’m trying to do and appears much more elegant.

http://sysoev.ru/nginx/docs/http/ngx_http_core_module.html#try_files

Use google translate :slight_smile: Yes it’s missing from the wiki :frowning:

I was under some impression that try_files could not take arguments
with a URI, which may be an assumption based on the name of the
directive. It’s not really documented that try_files can do this
either, unless I’m looking in the wrong place?

It’s explicitly stated in the wiki that you must specify the args
because in the try_files directive they’re not preserved as opposed to
what happens in a rewrite directive.

Core functionality

Right?

Yep.

Finally, exactly what happens if you have location blocks but none
of them match and you haven’t specified a default?

You should get a 404.

— appa

[email protected] writes:

Also your later config could be simplified:
server {

location / {
try_files $uri $uri/ /error.php?c=404 =404;
}
}

In this case, it seems to me that try files is looking for a file
named “/error.php?c=404” and not “/error.php”. Try_files does not
appear to strip the arguments from the uri when trying. My evidence
for this is this debug line:

2011/06/24 03:11:58 [debug] 20773#0: *1 try to use file:
“/error.php?c=403?”
“/home/www/testhome/error.php?c=403?”

This means that I can’t see if error.php exists before I try to throw an
error with it. Right now it seems that my solution is:

location / {
try_files $uri $uri/ =404;
}

location ~ .php$ {
<…fastcgi stuff…>
}

error_page 404 = /error.php?c=404;

location = /error.php {
try_files /error.php @low_level_404
<…the -same- fastcgi stuff as above…>
}

location @low_level_404 {
return 404;
}

I am really trying hard not to use “if” but I am weakening here because
an “if” would let me use one and only one instance of fastcgi:

location / {
try_files $uri $uri/ =404;
}

location ~ .php$ {
<…fastcgi stuff…>
}

error_page 404 = @error_404
location @error_404 {
if (! -f $document_root/error.php) {
return 404;
}
rewrite ^ $scheme://$server_name/error.php?c=404
}

I’m also curious (in an academic way) as to what use case exists when
try_files has to match url arguments as a filename? I usually strongly
discourage the use of ‘?’ and ‘&’ characters in a filename.

Dave H. - Consultant - Altadena CA, USA - [email protected]

The opinions expressed above are entirely my own <<<

Treat people as if they are what they ought to be, and you
help them to become what they are capable of being.

On 24 Jun 2011 19h14 WEST, [email protected] wrote:

named “/error.php?c=404” and not “/error.php”. Try_files does not
appear to strip the arguments from the uri when trying. My evidence
for this is this debug line:

2011/06/24 03:11:58 [debug] 20773#0: *1 try to use file:
“/error.php?c=403?” “/home/www/testhome/error.php?c=403?”

No. It doesn’t. Check the code. It tries a named location otherwise
invokes ngx_http_split_args(). That’s just a concise way of writing a
debug line in the error log.

This means that I can’t see if error.php exists before I try to
throw an error with it. Right now it seems that my solution is:

Yes you can.

location = /error.php {
try_files /error.php @low_level_404
<…the -same- fastcgi stuff as above…>
}

This doesn’t make sense. This is an exact location. It’s entered from
a try_files, a rewrite or when you request that file directly. If the
file doesn’t exist it signals a 404.

}

location ~ .php$ {
<…fastcgi stuff…>
}

No need for an if. At least that’s what I conclude from the previous
discussion. This should be all you need for the situation you
presented.

location / {
try_files $uri $uri/ /error.php?c=404 =404;
}

location = /error.php {
(…) # fastcgi stuff
}

I’m also curious (in an academic way) as to what use case exists
when try_files has to match url arguments as a filename? I usually
strongly discourage the use of ‘?’ and ‘&’ characters in a filename.

You’re taking the peculiar way the debug line is written as the real
way things are done.

Here’s the relevant code from the http_core_module.c source (starting
at line 1241):

ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
“try to use %s: “%s” “%s””,
test_dir ? “dir” : “file”, name, path.data);

— appa

[email protected] writes:

No. It doesn’t. Check the code. It tries a named location otherwise
invokes ngx_http_split_args(). That’s just a concise way of writing a
debug line in the error log.

Ok, I checked the code. As far as I can tell, it uses the actual
path output in the debug line. The code only calls the split_args()
thing is if tf->lengths == NULL and tf->name.len == 0…and also
if that is true then it appears to redirect:

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "try to use %s: \"%s\" \"%s\"",
                   test_dir ? "dir" : "file", name, path.data);

    if (tf->lengths == NULL && tf->name.len == 0) {

        if (tf->code) {
            ngx_http_finalize_request(r, tf->code);
            return NGX_OK;
        }

        path.len -= root;
        path.data += root;

        if (path.data[0] == '@') {
            (void) ngx_http_named_location(r, &path);

        } else {
            ngx_http_split_args(r, &path, &args);

            (void) ngx_http_internal_redirect(r, &path, &args);
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_OK;
    }
    ...
    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, 

r->pool)
!= NGX_OK)
{

If I’m correct (and I might not be) if split_args() gets called then
an internal redirect is done to that path.

I checked this two ways.

a) I compared the debug output of

try_files /error.php @some_other_location

2011/06/24 15:40:33 [debug] 66904#0: *1 try files phase: 11
2011/06/24 15:40:33 [debug] 66904#0: *1 try to use file: “/error.php”
“/home/test/error.php”
2011/06/24 15:40:33 [debug] 66904#0: *1 try file uri: “/error.php”

with

try_files /error.php?c=404 @some_other_location

2011/06/24 15:39:05 [debug] 66770#0: *1 try to use file:
“/error.php?c=403”
“/home/test/error.php?c=403”
2011/06/24 15:39:05 [debug] 66770#0: *1 try to use file:
@real_error_403” “/home/test/@real_error_403

b) I added to the code this:

  •   ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    
  •                   "really truly try to use %s: \"%s\" \"%s\"",
    
  •                   test_dir ? "dir" : "file", name, path.data);
      if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, 
    

r->pool)

So it’s right above the open (which ostensibly does the file check).
This gives me:

2011/06/24 16:00:15 [debug] 70121#0: *1 try files phase: 11
2011/06/24 16:00:15 [debug] 70121#0: *1 try to use file:
“/error.php?c=403”
“/home/test/error.php?code=403”
2011/06/24 16:00:15 [debug] 70121#0: *1 really truly try to use file:
“/error.php?c=403” “/www/home/test/website/error.php?code=403”
2011/06/24 16:00:15 [debug] 70121#0: *1 try to use file:
@real_error_403
“//home/test/@real_error_403

Even with this evidence, I don’t know the code all that well so if I’m
missing something please correct me. :slight_smile: I am using nginx 1.0.3, maybe
that is the issue?

Dave H. - Consultant - Altadena CA, USA - [email protected]

The opinions expressed above are entirely my own <<<

The concept, the label, is perpetually hiding from us all
the nature of the real. --Joyce Cary