Welcome! Log In Create A New Profile

Advanced

How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

agentzh
February 14, 2011 12:30AM
On Tue, Feb 1, 2011 at 11:45 PM, Ryan Malayter <malayter@gmail.com> wrote:
>
> It does in fact work in production on nginx 0.7.6x. Below is my actual
> configuration (trimmed to the essentials and with a few substitutions
> of actual URIs).
>

Well, ngx_proxy module's directive inheritance is in action here,
which gives you nice side effects that you want :)

I'll analyze some examples here such that people *may* get some light.

[Case 1]

location /proxy {
set $a 32;
if ($a = 32) {
set $a 56;
}
set $a 76;
proxy_pass http://127.0.0.1:$server_port/$a;
}

location ~ /(\d+) {
echo $1;
}

Calling /proxy gives 76 because it works in the following steps:

1. Nginx runs all the rewrite phase directives in the order that
they're in the config file, i.e.,

set $a 32;
if ($a = 32) {
set $a 56;
}
set $a 76;

and $a gets the final value of 76.

2. Nginx traps into the "if" inner block because its condition $a = 32
was met in step 1.

3. The inner block does not has any content handler, ngx_proxy
inherits the content handler (that of ngx_proxy) in the outer scope
(see src/http/modules/ngx_http_proxy_module.c:2025).

4. Also the config specified by proxy_pass also gets inherited by the
inner "if" block (see src/http/modules/ngx_http_proxy_module.c:2015)

5. Request terminates (and the control flow never goes outside of the
"if" block).

That is, the proxy_pass directive in the outer scope will never run in
this example. It is "if" inner block that actually serves you.

Let's see what happens when we override the inner "if" block's content
handler with out own:

[Case 2]

location /proxy {
set $a 32;
if ($a = 32) {
set $a 56;
echo "a = $a";
}
set $a 76;
proxy_pass http://127.0.0.1:$server_port/$a;
}
location ~ /(\d+) {
echo $1;
}

You will get this while accessing /proxy:

a = 76

Looks counter-intuitive? Oh, well, let's see what's happening this time:

1. Nginx runs all the rewrite phase directives in the order that
they're in the config file, i.e.,

set $a 32;
if ($a = 32) {
set $a 56;
}
set $a 76;

and $a gets the final value of 76.

2. Nginx traps into the "if" inner block because its condition $a = 32
was met in step 1.

3. The inner block *does* has a content handler specified by "echo",
then the value of $a (76) gets emitted to the client side.

4. Request terminates (and the control flow never goes outside of the
"if" block), as in Case 1.

We do have a choice to make Case 2 work as we like:

[Case 3]

location /proxy {
set $a 32;
if ($a = 32) {
set $a 56;
break;

echo "a = $a";
}
set $a 76;
proxy_pass http://127.0.0.1:$server_port/$a;
}
location ~ /(\d+) {
echo $1;
}

This time, we just add a "break" directive inside the if block. This
will stop nginx from running the rest ngx_rewrite directives. So we
get

a = 56

So this time, nginx works this way:

1. Nginx runs all the rewrite phase directives in the order that
they're in the config file, i.e.,

set $a 32;
if ($a = 32) {
set $a 56;
break;
}

and $a gets the final value of 56.

2. Nginx traps into the "if" inner block because its condition $a = 32
was met in step 1.

3. The inner block *does* has a content handler specified by "echo",
then the value of $a (56) gets emitted to the client side.

4. Request terminates (and the control flow never goes outside of the
"if" block), just as in Case 1.

Okay, you see how ngx_proxy module's config inheritance among nested
locations take the key role here, and make you *believe* it works the
way that you want. But other modules (like "echo" mentioned in one of
my earlier emails) may not inherit content handlers in nested
locations (in fact, most content handler modules, including upstream
ones, don't).

And one must be careful about bad side effects of config inheritance
of "if" blocks in other cases, consider the following example:

[Case 5]

location /proxy {
set $a 32;
if ($a = 32) {
return 404;
}
set $a 76;
proxy_pass http://127.0.0.1:$server_port/$a;
more_set_headers "X-Foo: $a";
}
location ~ /(\d+) {
echo $1;
}

Here, ngx_header_more's "more_set_headers" will also be inherited by
the implicit location created by the "if" block. So you will get:

curl localhost/proxy
HTTP/1.1 404 Not Found
Server: nginx/0.8.54 (without pool)
Date: Mon, 14 Feb 2011 05:24:00 GMT
Content-Type: text/html
Content-Length: 184
Connection: keep-alive
X-Foo: 32

which may or may not what you want :)

BTW, the "add_header" directive will not emit a "X-Foo" header in this
case, and it does not mean no directive inheritance happens here, but
add_header's header filter will skip 404 responses.

You see, how tricky it is behind the scene! No wonder people keep
saying "nginx's if is evil".

Cheers,
-agentzh

Disclaimer: There may be other corner cases that I've missed here, and
other more knowledgeable people can correct me wherever I'm wrong :)

_______________________________________________
nginx mailing list
nginx@nginx.org
http://nginx.org/mailman/listinfo/nginx
Subject Author Posted

How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

agentzh February 14, 2011 12:30AM

Re: How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

António P. P. Almeida February 14, 2011 05:32PM

Re: How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

Eugaia February 14, 2011 06:52PM

Re: How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

Ryan Malayter February 15, 2011 02:04PM

Re: How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

Dayo February 15, 2011 02:23PM

Re: How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

Dayo February 15, 2011 02:27PM

Re: How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

agentzh February 15, 2011 11:28PM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

Alexander Kunz February 18, 2011 06:22AM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

agentzh February 18, 2011 06:58AM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

agentzh February 18, 2011 06:58AM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

Alexander Kunz February 18, 2011 07:34AM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

Justin Cormack February 18, 2011 08:26AM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

agentzh February 19, 2011 01:10AM

Re: How nginx's "location if" works (Was Re: Setting cache parameter via if directives)

Alexander Kunz February 19, 2011 05:46AM



Sorry, only registered users may post in this forum.

Click here to login

Online Users

Guests: 107
Record Number of Users: 6 on February 13, 2018
Record Number of Guests: 214 on March 20, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready