nginx 的 if 指令功能有限且易误用,仅限在 location 块内使用 return、rewrite 或 set,禁止 proxy_pass、文件判断及 server 块顶层使用,推荐用 map、try_files、limit_except 等更安全替代方案。

Nginx 的 if 指令功能有限但容易误用,实际配置中稍有不慎就可能引发 404、重定向循环、变量未生效甚至 worker 崩溃。它不是编程语言里的 if,而是一个受上下文严格限制的 rewrite 模块指令。
语法与基本规则
必须在 server 或 location 块内使用,不能嵌套,不支持 &&、|| 等逻辑运算符,也没有 else 分支。条件括号前必须留空格,否则会报 unknown directive 错误:
-
if ($arg_id) { ... }✅ 正确(变量非空即真) -
if($arg_id){ ... }❌ 报错(缺少空格) -
if ($request_method = POST) { ... }✅ 注意等号两侧都要有空格 -
if ($http_user_agent ~* "curl|wget") { return 403; }✅ 不区分大小写正则匹配
哪些判断是允许且相对安全的
仅限以下三类操作,且建议都放在 location 块内:
- 用
return终止请求(如返回 403/405/301) - 用
rewrite ... last或rewrite ... permanent做简单跳转 - 用
set设置变量(但要注意:该变量在 if 外部不一定立即可用,尤其不能依赖它做后续 proxy_pass)
例如禁止非标准请求方法:
location / {
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}
}
绝对禁止的用法
这些写法看似合理,实则高危:
- 在
if块里写proxy_pass、fastcgi_pass或add_header—— 指令会被忽略或触发未定义行为 - 用
-f、-d等文件系统判断(如if (-f $request_filename))—— 应改用try_files - 在
server块顶层使用if做域名跳转(如强制 www)—— 推荐拆成两个server块 - 多个
if并列判断同一请求参数(如先判$arg_a再判$arg_b)—— 无短路机制,逻辑难控
更推荐的替代方案
绝大多数场景都有更稳定、更高效的选择:
- 多条件分流或用户代理识别 → 用
map预计算变量(如map $http_user_agent $is_bot) - 静态资源存在性检查 → 用
try_files $uri $uri/ =404 - 请求方法限制 → 用
limit_except(如只允 GET/HEAD) - 路径重写 → 直接用
rewrite指令,不套if - IP 访问控制 → 用
allow/deny,比 if + return 更可靠
真正需要 if 时,务必加注释说明触发条件,并用 curl -I 测试边界情况:空参数、大小写、URL 编码字符等。











