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-09-17 03:30:06

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

nginx location 正则配置求助

问题描述 nginx location 正则配置求助 nginx location 如何配置使得其匹配 /admin/*.do和/admin/*.jsp 注:apache中我以前是这样配置的,现在要把apache换成nginx JkMount /admin/*.jsp tomcatadmin JkMount /admin/*.do tomcatadmin 解决方案 location ~* /admin/*.do location ~* /admin/*.jsp

nginx location语法使用介绍_nginx

nginx location介绍 Nginx 中的 Location 指令 是NginxHttpCoreModule中重要指令.Location 指令,是用来为匹配的 URI 进行配置,URI 即语法中的"/uri/",可以是字符串或正则表达式.但如果要使用正则表达式,则必须指定前缀. nginx location语法 基本语法:location [=|~|~*|^~] /uri/ { - } = 严格匹配.如果这个查询匹配,那么将停止搜索并立即处理此请求. ~ 为区分大小写匹配(可用

nginx location语法使用说明

nginx location介绍 Nginx 中的 Location 指令 是NginxHttpCoreModule中重要指令.Location 指令,是用来为匹配的 URI 进行配置,URI 即语法中的"/uri/",可以是字符串或正则表达式.但如果要使用正则表达式,则必须指定前缀. nginx location语法 基本语法:location [=|~|~*|^~] /uri/ { - } = 严格匹配.如果这个查询匹配,那么将停止搜索并立即处理此请求. ~ 为区分大小写匹配(可用

nginx location在配置中的优先级

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

Nginx Location 指令简明指南_nginx

Nginx 中的 Location 指令 是NginxHttpCoreModule中重要指令.Location 指令比较简单,但却是配置 Nginx 过程中不得不去了解的. Location 指令,是用来为匹配的 URI 进行配置,URI 即语法中的"/uri/",可以是字符串或正则表达式.但如果要使用正则表达式,则必须指定前缀. 一.基本语法 1.location [=|~|~*|^~|@] /uri/ { - } [=] 表示精确匹配,如果找到,立即停止搜索并立即处理此请求. [~

nginx location如何消除重复配置项?

问题描述 例如有如下两个location配置location ~ /xxx/.+$ { proxy_pass http://tomcat; proxy_set_header Host $host; proxy_set_header X-URI $request_uri; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-S

nginx location 指令匹配顺序

location 指令语法如下:  代码如下 复制代码 location [=|^~|~|!~|~*|!~*] uri { } 上述配置中[]内为可选配置,但只能使用其中一个.关于具体含义,可以参考:nginx 匹配操作符.uri可以是普通字符串或正则表达式. 匹配的顺序是先匹配普通字符串,然后再匹配正则表达式.另外普通字符串匹配顺序是根据配置中字符长度从长到短,也就是说使用普通字符串配置的location顺序是无关紧要的,反正最后nginx会根据配置的长短来进行匹配,但是需要注意的是正则表达式

NGINX location 在配置中的优先级

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

nginx location 配置方法总结

一.location [=|~|~*|^~|@] /uri/ { ... } 1.~ 为区分大小写匹配  2.~* 为不区分大小写匹配  3.!~ 为区分大小写不匹配 4.!~* 为不区分大小写不匹配  5.= 开头表示精确匹配 6./ 通用匹配,所有请求都会被匹配到 有多个location配置的情况下,先走 = ,然后是^~,其他的则按书写顺序来依次匹配,最后是通用匹配 / ;  代码如下 复制代码 nginx不对url进行编码,例如:/20%/ 可以被 / / 匹配到. 示例: 匹配所有请求