Nginx的if

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

访问/case1时会返回76.

Nginx 是这样工作的:
1. Nginx先按照配置顺序执行所有的rewrite指令:

$a最后值是76

  1. 因为条件$a = 32成立,所以Nginx就进入if的内部块。
  2. 这个内部块没有任何内容处理器(content handler),ngx_proxy 就会继承外部块的内容处理器(content handler)。(see src/http/modules/ngx_http_proxy_module.c:2025).
  3. 同时配置指定的proxy_pass也会被if内部块继承。(see src/http/modules/ngx_http_proxy_module.c:2015)
  4. 请求结束。(其实流程进了if块再也没出来)

所以,外面的proxy_pass指令并没有执行,真正提供服务的是if内部块。

如果我们的if块声明了内容处理器(content handler)会发生什么
Case2

当我们访问/case2时,我们会得到

这中间都发生了什么:
1. Nginx先按照配置顺序执行所有的rewrite指令:

$a最后值是76

  1. 因为条件$a = 32成立,所以Nginx就进入if的内部块。
  2. 这个内部块有内容处理器(content handler)echo,所以$a的值(a = 76)就返回给了客户端。
  3. 请求结束。(流程进了if块再也没出来,和Case1一样)

我们再调整一下:
Case 3

~ curl localhost/case3
a = 56

$a最后值是56

  1. 因为条件$a = 32成立,所以Nginx就进入if的内部块。
  2. 这个内部块有内容处理器(content handler)echo,所以$a的值(a = 56)就返回给了客户端。
  3. 请求结束。(流程进了if块再也没出来,和Case1一样)

还有一个if块继承配置的场景:
case 4:

访问的时候需要添加-i参数,这样就可以打印出HTTP响应头。

只有X-Foo Header被添加了,X-Bar Header却没有
这个可能是你想要的或者不是
这里ngx_header_moremore_set_headers也会被继承
add_header指令虽然忽略X-Foo header,但是它依旧被继承了,是因为add_header的header filter会忽略4XX响应。 :-p

注: echo,ngx_header_more模块都是三方模块,如果需要测试以上示例,建议使用openresty.

或者直接使用 docker 测试

相关博客:
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