How nginx "location if" works

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

时间: 2024-10-11 13:38:55

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

nginx location配置

nginx location配置 location在nginx中起着重要作用,对nginx接收到的请求字符串进行处理,如地址定向.数据缓存.应答控制.代理转发等 location语法 location [=|~|~*|^~] uri {...} []部分是匹配类型,可以没有,其中各项含义: (1)= 精准匹配,请求字符串必须和uri完全相同时匹配成功 (2)~ 区分大小写的正则匹配 (3)~* 不区分大小写的正则匹配 (4)^~ 注意这个不是正则匹配,表示uri以普通字符串开头, (5)空 表示

nginx location 规则优先级

一 nginx  location 匹配命令 ~   #表示执行一个正则匹配,区分大小写 ~*  #表示执行一个正则匹配,不区分大小写 ^~  #表示普通字符匹配,如果该选项匹配,只匹配该选项, 不匹配别的选项,一般用来匹配目录 =   #进行普通字符精确匹配 二 nginx location 匹配优先级(与location在配置文件中的顺序无关) 1=  精确匹配.如果发现精确匹配,nginx停止搜索其他匹配模式. 2    普通字符匹配,正则表达式规则和长的块规则将被优先和和查询匹配,也就是

nginx location一些认识

基本语法 location [=|~|~*|^~|@] /uri/ { - } [=]表示精确匹配,如果找到,立即停止搜索并立即处理此请求. [~ ] 表示区分大小写匹配 [~*] 表示不区分大小写匹配 [^~ ] 表示只匹配字符串,不查询正则表达式. [@] 指定一个命名的location,一般只用于内部重定向请求. **区分和不区分大小写的正则匹配优先级相同,以先后顺序来决定匹配哪一个 **=的优先级比~高 **^~的优先级比~高 **^~优先匹配的是从根开始的匹配 nginx locati

Nginx Location和Rewrite深入剖析

Nginx Location和Rewrite深入剖析 Nginx Location Nginx由内核和模块组成,其中内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端的请求映射到一个location block,而location是Nginx配置中的一个指令,用于访问的URL匹配,而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作. location功能是由ngx_http_index_module模块提供的. location常放在serve

nginx location配置(URL)

语法规则: location [=|~|~*|^~] /uri/ { … }= 表示精确匹配,这个优先级也是最高的^~ 表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格).~  表示区分大小写的正则匹配~* 表示不区分大小写的正则匹配(和上面的唯一区别就是大小写)!~和!~*分别为区分大小写不匹配及不区分大小写不匹配的正则/ 通用匹配,任何请求都会匹配到,默认

Nginx Location指令配置及常用全局变量

在实践安装nginx的时候,不知道./configure是什么意思,这里特地记录一下. 在linux中./代表当前目录,属于相对路径../代表上一级目录,属于相对路径/代表根目录,/开头的文件都是绝对路径./configure的意思是执行当前目录下面的configure文件configure一般都有可执行的权限,如果没有的话,用./configure是不能执行的,但是可以这样执行:sh ./configure 使用--prefix参数指定nginx安装的目录 不指定prefix,则可执行文件默认

2019.9.20 Nginx Location 及静动分离案例

一.Nginx正则及location匹配1.Nginx location规则匹配^~:标识符匹配后面跟一个字符=:精准匹配~:区分大小写的匹配~*:不区分大小写的匹配!~:对区分大小写的匹配取非!~*:对不区分大小写的匹配取非/:通用匹配2.正则表达式*:重复前面的字符0次或多次?:重复前面的字符0次或1次+:重复前面的字符1次或多次.:匹配除换行符以外的任意一个字符(a|b):匹配a或b^:以...开头$:以...结尾{n}:重复前面的字符n次{n,}:重复前面的字符n次或更多次{n,m}:重

nginx location在配置中的优先级

location表达式类型 ~     表示执行一个正则匹配,区分大小写 ~*    表示执行一个正则匹配,不区分大小写 ^~    表示普通字符匹配.使用前缀匹配.如果匹配成功,则不再匹配其他location. =     进行普通字符精确匹配.也就是完全匹配. @     “@” 定义一个命名的 location,使用在内部定向时,例如 error_page, try_files location优先级说明 在nginx的location和配置中location的顺序没有太大关系.正loca

Nginx Location配置总结

http://blog.sina.com.cn/s/blog_97688f8e0100zws5.html 语法规则: location [=|~|~*|^~] /uri/ { … }= 开头表示精确匹配^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格).~ 开头表示区分大小写的正则匹配~*  开头表示不区分大小写的正则匹配!~和!~*分别为区分大小写