# Nginx 中 location 块内 if 指令失效问题


## 问题描述

访问 `https://example.com/usr/life_sqlite.db` 时，请求一直返回 302 重定向，最终导致 "Maximum redirects followed" 错误。

配置文件 `/etc/angie/rewrite/typecho.conf` 原内容：

<!--more-->

```nginx
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` 时的执行流程：

1. 请求匹配 `location /`
2. 理论上：`if ($uri ~* \.db$)` 应该返回 403
3. 实际上：`if` 内的 `return` 没有生效
4. 继续执行后续的 rewrite 规则
5. 文件不存在 → rewrite 到 `/index.php/test.db` → 触发 PHP 处理 → 重定向循环

## 尝试过的失败方案

1. 使用 `location ^~ /usr/` 前缀匹配 → 失败
2. 将 `location ~* \.db$` 放在 `location /` 之前 → 失败
3. 使用 `set` 变量 + `if` 判断 → 失败
4. 使用 `break` 关键字 → 失败
5. 直接在 server 块中添加 `location ~* \.db$` → 失败

## 最终解决方案

使用 `error_page` 技巧配合命名 location，绕过 if 指令的执行问题：

```nginx
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;
}
```

## 测试结果

```bash
$ curl -sI "https://example.com/test.db"
HTTP/2 403

$ curl -sI "https://example.com/usr/life_sqlite.db"
HTTP/2 403
```

## 关键知识点

1. **Nginx location 匹配优先级**：
   - `=` 精确匹配
   - `^~` 前缀匹配（不检查正则）
   - `~` 和 `~*` 正则匹配
   - 普通前缀匹配

2. **if 指令的局限性**：
   - 在 `location` 块内的 `if` 指令行为不可预测
   - 尽量避免在 `location` 内使用复杂 if 条件
   - 优先使用 `error_page` 或独立的 location 块

3. **error_page 技巧**：
   - 使用不存在的状态码（如 418）作为中间跳转
   - 配合命名 location（`@name`）实现条件分支
   - 可以在 rewrite 阶段之后生效

## 参考资料

- Nginx 官方文档：<https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if>

