博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
How nginx "location if" works
阅读量:6446 次
发布时间:2019-06-23

本文共 5389 字,大约阅读时间需要 17 分钟。

Nginx's if directive does have some weirdness in practice. And people may misuse it when they do not have enough knowledge about its behavior. In this post, I'll analyze some examples here such that people may get some light and use it correctly.

In short, Nginx's "if" block effectively creates a (nested) location block and once the "if" condition matches, only the content handler of the inner location block (i.e., the "if" block) will be executed.

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 4

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 an 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 "if is evil".

We've been using the ngx_lua module to do such complicated nginx.conf branching (and also the whole application's business logic) in Lua. Lua's "if" is not evil anyway.

For ngx_lua's set_by_lua directive, there's even no Lua coroutine overhead (though the overhead itself is very small).

Please note that I did not say that you should never use nginx's "if". Don't take me wrong. My motivation of writing this explanation of the underlying mechanism is to help you use it correctly and wisely ;)

I think Igor Sysoev will redesign the whole rewrite module in his nginx 2.0 devel branch. Then everything here will be changed.

P.S. This article was originally posted to this nginx mailing list thread: http://forum.nginx.org/read.php?2,174917

http://agentzh.blogspot.com/2011/03/how-nginx-location-if-works.html

 

转载地址:http://qmvwo.baihongyu.com/

你可能感兴趣的文章
【HDU5909】Tree Cutting(FWT)
查看>>
你的名字高清视频分享
查看>>
PyalgoTrade 计算权重平滑平均价(三)
查看>>
Android 写文件到手机
查看>>
[BZOJ2820]YY的GCD
查看>>
mongoDB 索引
查看>>
【SpringBoot】SpringBoot项目的The temporary upload location ***is not valid 问题
查看>>
把页面的Table直接输出到Excel文件中
查看>>
Linux获取当前用户信息函数
查看>>
Bash shell
查看>>
2015移动安全挑战赛 第一题
查看>>
aspx页面@Page指令解析
查看>>
关于web项目中中文乱码问题的总结
查看>>
ios 之CGRectMake
查看>>
选择排序的算法和优化
查看>>
DataGridView 输入数据验证格式(实例)
查看>>
HDOJ 2151
查看>>
Foundation框架 - 快速创建跨平台的网站页面原型
查看>>
Intel 82599网卡异常挂死原因
查看>>
open-falcon
查看>>