Nginx 中 location 块内 if 指令失效问题
目录
问题描述
访问 https://example.com/usr/life_sqlite.db 时,请求一直返回 302 重定向,最终导致 “Maximum redirects followed” 错误。
配置文件 /etc/angie/rewrite/typecho.conf 原内容:
location / {
if ($uri ~* \.db$) {
return 403;
}
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php$1 last;
}
}
location /usr/ {
location ~* \.db$ {
deny all;
return 403;
}
}根本原因
Nginx 的 “if is evil” 问题
Nginx 的 if 指令与 location 块组合使用时,行为不可预测。这是 Nginx 长期存在的已知问题,官方文档甚至警告 “if is evil”。
当请求 /test.db 时的执行流程:
- 请求匹配
location / - 理论上:
if ($uri ~* \.db$)应该返回 403 - 实际上:
if内的return没有生效 - 继续执行后续的 rewrite 规则
- 文件不存在 → rewrite 到
/index.php/test.db→ 触发 PHP 处理 → 重定向循环
尝试过的失败方案
- 使用
location ^~ /usr/前缀匹配 → 失败 - 将
location ~* \.db$放在location /之前 → 失败 - 使用
set变量 +if判断 → 失败 - 使用
break关键字 → 失败 - 直接在 server 块中添加
location ~* \.db$→ 失败
最终解决方案
使用 error_page 技巧配合命名 location,绕过 if 指令的执行问题:
location / {
error_page 418 =403 @db_deny;
if ($uri ~* \.db$) { return 418; }
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php$1 last;
}
}
location @db_deny {
return 403;
}测试结果
$ curl -sI "https://example.com/test.db"
HTTP/2 403
$ curl -sI "https://example.com/usr/life_sqlite.db"
HTTP/2 403关键知识点
Nginx location 匹配优先级:
=精确匹配^~前缀匹配(不检查正则)~和~*正则匹配- 普通前缀匹配
if 指令的局限性:
- 在
location块内的if指令行为不可预测 - 尽量避免在
location内使用复杂 if 条件 - 优先使用
error_page或独立的 location 块
- 在
error_page 技巧:
- 使用不存在的状态码(如 418)作为中间跳转
- 配合命名 location(
@name)实现条件分支 - 可以在 rewrite 阶段之后生效