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

How nginx "location if" works的更多相关文章

  1. Nginx location 匹配顺序整理

    Nginx location模块整理 具体的Nginx安装就不在这里描述了,这里只是为了对location的描述 Nginx环境 a. 查看当前系统cat /etc/redhat-release [r ...

  2. Nginx Location配置总结

    Nginx Location配置总结 语法规则: location [=|~|~*|^~] /uri/ { - }= 开头表示精确匹配^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即 ...

  3. nginx location配置

    nginx location配置   location在nginx中起着重要作用,对nginx接收到的请求字符串进行处理,如地址定向.数据缓存.应答控制.代理转发等location语法location ...

  4. nginx location的配置

    文章转自:http://www.ttlsa.com/nginx/nginx-location-configure/ location的语法配置规则: 语法规则: location [=|~|~*|^~ ...

  5. nginx location配置(URL)

    语法规则: location [=|~|~*|^~] /uri/ { … }= 表示精确匹配,这个优先级也是最高的^~ 表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url ...

  6. Nginx location配置详细解释

    nginx location配置详细解释 语法规则: location [=|~|~*|^~] /uri/ { - } = 开头表示精确匹配 ^~ 开头表示uri以某个常规字符串开头,理解为匹配 ur ...

  7. nginx location匹配顺序及CI框架的nginx配置

    Nginx location匹配顺序如下: 用前缀字符串定义的location规则对URI进行匹配测试. =号定义了精确的前缀字符串匹配,如果发现精确匹配则使用当前规则.否则继续下一步匹配. 匹配其它 ...

  8. nginx Location 语法基础知识

    URL地址匹配是Nginx配置中最灵活的部分 Location 支持正则表达式匹配,也支持条件匹配,用户可以通过location指令实现Nginx对动丶静态网页的过滤处理. Nginx locatio ...

  9. nginx location 正则匹配

    nginx 统计语句1.根据访问IP统计UV awk '{print $1}' access.log|sort | uniq -c |wc -l2.统计访问URL统计PV awk '{print $7 ...

随机推荐

  1. 【Android 应用开发】AndroidUI设计之 布局管理器 - 详细解析布局实现

    写完博客的总结 : 以前没有弄清楚的概念清晰化 父容器与本容器属性 : android_layout...属性是本容器的属性, 定义在这个布局管理器的LayoutParams内部类中, 每个布局管理器 ...

  2. LeetCode之“动态规划”:Interleaving String

    题目链接 题目要求: Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example ...

  3. rails自动生成大量记录的方法

    因为我们可能rails new了一个网站出来,但是里面没有测试数据,我们不能傻乎乎的在new.html.erb里面一个的手动输入吧?于是我们可以写一个小的脚本来帮助在数据库中插入大量数据:高版本的ra ...

  4. 恶补web之六:javascript知识(1)

    javascript(下称js)是一种轻量级编程语言,它可以插入html页面然后由浏览器执行. document.write("<h1>...</h1>") ...

  5. gcc或clang中消除特定警告的方法

    一般在编译代码时会有相当多的警告信息,尤其当我们使用了-Wall选项的时候.-Wall绝不是像其字面意思一样打开所有警告.不过它打开的警告也相当多了.对于一些我们已知"无害"但仍然 ...

  6. Mac Finder 里新建文本

    Mac Finder 里新建文本 首先吐槽下 Mac 的文件管理 Finder 真的是太弱了,之前没感觉 Windows 的资源管理器多厉害,但是和 Mac 的比起来真是堪称神器啊,果然牛逼与否还的看 ...

  7. Viavdo&ISE&Quartus II级联Modelsim级联仿真

    博主一直致力寻找高效的工作方式,所以一直喜欢折腾软件,从刚开始只用软件IDE自带的编辑器,到Notepad++,再到后来的Vim,从用ISE14.7自带的Isim仿真,到发现更好的Modelsim,再 ...

  8. 彪悍开源的分析数据库-ClickHouse

    https://zhuanlan.zhihu.com/p/22165241 今天介绍一个来自俄罗斯的凶猛彪悍的分析数据库:ClickHouse,它是今年6月开源,俄语社区为主,好酒不怕巷子深. 本文内 ...

  9. Python循环依赖问题的解决

    一个是把某个import移到代码中间,使原先的循环依赖圈打开.

  10. C#将一个枚举里面所有描述和value绑定到下拉列表的方法

    /// <summary> /// 获取枚举值的描述,如果没有描述,则返回枚举名称 /// </summary> /// <param name="en&quo ...