Precedence return directive and nested locations
Maxim Dounin
mdounin at mdounin.ru
Wed Aug 24 20:37:32 UTC 2022
Hello!
On Wed, Aug 24, 2022 at 07:38:02PM +0200, Dipl. Ing. Sergey Brester via nginx-devel wrote:
> it seems that the question of precedence of non-conditional _return_
> directive vs nested _location_s is not really clear,
> or rather some constellations (like fallback) are impossible or else the
> configuration may look weird.
>
> For instance:
>
> server {
> server_name ...;
>
> location ~ ^/(some-uri|other-uri) {
> return 200 ...;
> }
>
> # fallback for any not matched uri:
> return 444;
> }
>
> will ALWAYS return with 444 for that server no matter whether it matches
> the location(s) or doesn't.
> Basically the locations are totally superfluous here (despite
> specified), considering the return is non-conditional.
>
> Sure, the documentation says "Stops processing and returns the specified
> _code_ to a client.",
> but normally the nested locations (if matched) have always higher
> precedence over anything else
> in the parent location.
>
> Furthermore the docu of ngx_http_rewrite_module [1] says at begin:
>
> * the directives of this module specified on the server [2] level are
> executed sequentially;
>
> * repeatedly:
>
> * a location [3] is searched based on a request URI;
> * the directives of this module specified inside the found location
> are executed sequentially;
>
> What is a bit ambiguous. But if it is to understand directly - it is
> clear that a return directive at server level
> bypasses all other locations (sequence in current level before its
> locations).
> Just it makes hardly possible (up to impossible depending on location
> tree) to use return directive for a fallback case.
>
> To implement the fallback return, one can surely pack this into a
> location like:
>
> location ~ .* { return 444; }
> Just it is a bit ugly in my opinion, let alone it would quasi disallow
> the usage of longest match prefix locations,
> because they work only if no match with a regular expression is found
> (then the configuration of the
> prefix location remembered earlier is used).
>
> So I assume:
>
> * either it is a lack of documentation (and it must get a hint about
> the precedence)
> and/or still better a description how one could achieve such a
> "fallback" return (location or whatever).
> (but do we have at all a possibility to specify such proper fallback
> location matching at end,
> if nothing else matched?)
>
> * or (improbably) this is a flaw and must be "fixed" or enhanced rather
> for a non-conditional return case
> (unsure it wouldn't introduce some compat-issue);
>
> Any thoughts?
As of now, handling of the rewrite module directives, including
"return", is explicitly defined and documented. In particular,
rewrite module directives at the server level are handled
differently than location-level rewrite directives, and this
happens before any location matching. This is to make it possible
to change URI (or reject requests) at the very start of the
request processing, before any location matching. And I believe
the documentation is clear enough on this.
For fallback actions, consider using a generic location with
appropriate action. Usually something like
location / {
return 444;
}
is a good choice, since it is matched only if there are no other
matching locations, neither prefix nor defined by a regular
expression.
What can be confusing here is that rewrite directives at the server
level in a server block with locations behave differently from
rewrite directives in a location block with nested locations. For
example:
server {
rewrite /foo(/.*) /bar$1 break;
location /bar/ { return 200 ...; }
}
is quite different from:
server {
location / {
rewrite /foo(/.*) /bar$1 break;
location /bar/ { return 200 ...; }
}
}
since rewrite in "location /" will be executed after the location
matching, and only if the request is not matched by "location
/bar/" inside "location /". Similarly, "return" instead of
"rewrite" will unconditionally terminate processing of all request
in the server block:
server {
return 403;
location /bar/ { return 200 ...; }
}
but will not affect requests matched by inner locations if
specified in "location /":
server {
location / {
return 403;
location /bar/ { return 200 ...; }
}
}
I don't there is anything to fix here though.
--
Maxim Dounin
http://mdounin.ru/
More information about the nginx-devel
mailing list