Nginx的配置是一种声明式的配置
,虽然有if
指令,但是跟传统意义上的编程语言是完全不同的。在实际使用的过程中,使用if
指令会产生有很多很奇怪的事情,由其是在对它没有充分了解之前,经常会误用。Nginx官方也提醒在location
里使用if
是灾难,如果一定要使用if
,需要测试测试再测试. If is Evil… when used in location context 对于声明式的配置
来说: 1. Nginx的if
指令条件满足时,会在if
块里创建一个嵌套的子location
块,然后再执行if
块内的声明和内容处理器(content handler)。 2. 在一个配置块里只有一个if
语句和一些其他的声明,由于if
的存在,会导致有些声明不生效。为了保证这些声明都生效,最好在if
块内,重新声明一次。 3. 在一个配置块里,如果有两个if
指令都满足条件,只有第二个if
会被执行。 Case 1
1 | location /case1 { |
访问/case1
时会返回76.
1 | ~ curl localhost/case1 |
Nginx 是这样工作的: 1. Nginx先按照配置顺序执行所有的rewrite指令:
1 | set $a 32; |
$a最后值是76
- 因为条件
$a = 32
成立,所以Nginx就进入if
的内部块。 - 这个内部块没有任何内容处理器(content handler),ngx_proxy 就会继承外部块的内容处理器(content handler)。(see src/http/modules/ngx_http_proxy_module.c:2025).
- 同时配置指定的proxy_pass也会被
if
内部块继承。(see src/http/modules/ngx_http_proxy_module.c:2015) - 请求结束。(其实流程进了
if
块再也没出来)
所以,外面的proxy_pass
指令并没有执行,真正提供服务的是if
内部块。 如果我们的if
块声明了内容处理器(content handler)会发生什么 Case2
1 | location /case2 { |
当我们访问/case2
时,我们会得到
1 | ~ curl localhost/case2 |
这中间都发生了什么: 1. Nginx先按照配置顺序执行所有的rewrite指令:
1 | set $a 32; |
$a最后值是76
- 因为条件
$a = 32
成立,所以Nginx就进入if
的内部块。 - 这个内部块有内容处理器(content handler)
echo
,所以$a的值(a = 76)就返回给了客户端。 - 请求结束。(流程进了
if
块再也没出来,和Case1一样)
我们再调整一下: Case 3
1 | location /case3 { |
这一次我们在if
块内添加了break
指令. 它会阻止nginx的ngx_rewrite
指令的执行。因此我们得到
1 |
|
location /case4 {
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”;
add_header X-Bar $a;
}
location ~ /(\d+) {
echo $1;
}
1 |
|
~ curl -i localhost/case4
HTTP/1.1 404 Not Found
Server: openresty/1.19.9.1
Date: Thu, 09 Sep 2021 07:28:48 GMT
Content-Type: text/html
Content-Length: 159
Connection: keep-alive
X-Foo: 32
404 Not Found
1 |
|
相关博客: Nginx proxy_pass 的URL Mapping 参考资料: How nginx “location if” works Human & Machine How does if condition work inside location block in nginx conf? - Stack Overflow