nginx 内多个 if 仅一个生效问题

说明

  • 当前场景下,前端项目部署对 nginx 有两个要求:
    • 对类型是 html 的资源配置 Cache-Control “no-store”
    • 基于 IP 进行区分,符合条件的 IP,将请求转发至部署灰度服务的目录下
  • 针对 Cache-Control “no-store”,常规配置如下
1
2
3
4
5
6
7
8
location ~* ^/aaa/{
    if ($request_filename ~* .*\.(?:htm|html)$)
    {
       add_header Cache-Control "no-store";
    }

    root /app/tengine/html/xtest1 ;
}
  • 针对匹配 IP,常规配置如下
    • 使用 IF 判断存储请求 IP 的 Header,是否满足条件,转发至灰度目录
1
2
3
4
5
6
7
8
location ~* ^/aaa/{
    if ($proxy_add_x_forwarded_for ~* "(XX.XX.XX.XX|XXX.XXX.XXX.XXX)")
	{
      root /app/tengine/html/xtest ;
    }

    root /app/tengine/html/xtest1 ;
}
  • 同时配置后,发现灰度环境无法正常转发
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
location ~* ^/aaa/{
    if ($proxy_add_x_forwarded_for ~* "(XX.XX.XX.XX|XXX.XXX.XXX.XXX)")
	{
      root /app/tengine/html/xtest ;
    }

    if ($request_filename ~* .*\.(?:htm|html)$)
    {
       add_header Cache-Control "no-store";
    }

    root /app/tengine/html/xtest1 ;
}

问题定位

  • 通过注释、及调整顺序后发现,发现仅最后一个 if 生效
  • 感觉工作机制类似于每个 if 都会把之前 if 内的配置丢掉,类似于新开线程调继续执行后续代码

问题处理

Plan1:较常规的处理方案

  • 使用 map 代替第一个 if
  • http 层内定义 map,根据 IP 进行匹配,将对应的资源目录赋给变量
1
2
3
4
5
6
7
http {
	…………
	map $proxy_add_x_forwarded_for $aaa_root_path {
        "~*(XX.XX.XX.XX|XXX.XXX.XXX.XXX)" /app/tengine/html/xtest;
        default /app/tengine/html/xtest1;
    }
}
  • location 内进行配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
	…………
    location ~* ^/aaa/{
        if ($request_filename ~* .*\.(?:htm|html)$)
        {
           add_header Cache-Control "no-store";
        }

        root $aaa_root_path ;
    }
}

Plan2:针对当前场景也可解决问题的方案

  • 由于第一个 if 仅匹配其他资源,后续条件可以不走,可以取巧再第一个 if 内 break 掉
  • 这样也可实现匹配 IP 的请求进灰度目录,不匹配的走后续流程,但是进入灰度的资源加不上 Cache-Control “no-store”
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    location ~* ^/aaa/{
        if ($proxy_add_x_forwarded_for ~* "(XX.XX.XX.XX|XXX.XXX.XXX.XXX)"){
          root /app/tengine/html/xtest ;
          break;
        }

        if ($request_filename ~* .*\.(?:htm|html)$)
        {
           add_header Cache-Control "no-store";
        }

        root /app/tengine/html/xtest1 ;
    }

拓展:nginx if 生效情况

  • location 内多个 if,仅最后一个 if 生效
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 只有 X-Second 会被设置
location /only-one-if {
    set $true 1;

    if ($true) {
        add_header X-First 1;
    }

    if ($true) {
        add_header X-Second 2;
    }

    return 204;
}
  • if 前转、改写配置失效
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# proxy_pass 不会生效
location /proxy-pass-uri {
    proxy_pass http://127.0.0.1:8080/;

    set $true 1;

    if ($true) {
        # nothing
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# try_files 不会生效
location /if-try-files {
     try_files  /file  @fallback;

     set $true 1;

     if ($true) {
         # nothing
     }
}

参考