第03章:AI生成的限速配置,为什么没有生效?

第03章:AI生成的限速配置,为什么没有生效?

“你加了 limit_req_zone,但攻击者还是能每秒发送几百个请求——因为 limit_req 的位置放错了,或者 burst 设置让限速形同虚设。”


ℹ️ 版本说明:本章基于 Nginx 1.30.2 + ngx_http_limit_req_module(内置模块)。

3.1 AI默认会生成什么

你让 AI 帮你配置 API 限速,它通常给你:

http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    
    server {
        location /api/ {
            limit_req zone=api burst=20;
            proxy_pass http://127.0.0.1:8000;
        }
    }
}

这个配置的意图是:每个 IP 每秒最多10个请求,峰值允许20个(burst)。

但实际上,这个配置有两个问题:

  1. burst=20 没有 nodelay:当请求超过速率时,Nginx 会把它们排队(最多排20个),而不是立刻拒绝。结果:攻击者可以每秒发送 20+10=30 个请求,全部最终都能通过(只是有延迟)。
  2. 没有区分不同接口:登录接口(应该很严格)和普通 API(可以宽松)用同一个规则。

3.2 AI通常遗漏的4个坑

⚠️ 坑1:burst 和 nodelay 的混淆

limit_req zone=api burst=20;           # 超速请求排队,最终都通过
limit_req zone=api burst=5 nodelay;    # 超速请求立刻拒绝(429)

没有 nodelay 的 burst,是一个"缓冲区"——超出速率的请求会等待,但都会被处理。这不是真正的限速,而是"削峰"(延迟处理)。

对于防暴力破解的登录接口,你需要的是硬限速(超过立刻 429),而不是排队处理。


⚠️ 坑2:限速 zone 放在了错误的位置

http {
    limit_req_zone ...;   # 定义 zone,必须在 http 块
}

server {
    limit_req zone=api;   # 应用限速
    
    location / {
        limit_req zone=api;  # 如果同时在 server 和 location 里设置,只有更精确的(location)生效
    }
}

server 级别和 location 级别都有 limit_req 时,只有最精确的那个生效(location 优先)。AI 有时会在两个地方都加,但实际只有一个在工作。


⚠️ 坑3:用 $remote_addr 而不是 $binary_remote_addr

limit_req_zone $remote_addr zone=api:10m rate=10r/s;      # 字符串形式,最多15字节
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;  # 二进制形式,IPv4 4字节,IPv6 16字节

使用 $binary_remote_addr 可以节省大约 3-4 倍的内存,对于存储大量 IP 的 zone 来说有意义。AI 通常知道用 $binary_remote_addr,但不知道解释为什么。


⚠️ 坑4:没有 limit_req_status 自定义错误码

限速被触发时,Nginx 默认返回 503(Service Unavailable)。但 RFC 6585 定义了 429(Too Many Requests)专门用于限速。

客户端收到 503 会认为服务器崩溃了,应该收到 429 才知道是被限速了,可以稍后重试。

limit_req_status 429;

3.3 更好的提示词

提示词 P01:为不同接口配置分级限速

使用时机:API 服务需要对不同接口设置不同的限速规则

比默认多了什么

  • 不同接口不同规则(登录严格,普通 API 宽松)
  • nodelay 控制
  • 正确的错误码(429)
帮我为 Nginx 1.30.x 配置分级 API 限速。

接口类型和限速规则:
1. 登录/注册接口(/api/auth/login, /api/auth/register):
   - 同一 IP,每分钟最多 5 次
   - 超过立刻返回 429(硬限速,不排队)
   
2. 发送验证码接口(/api/auth/send-sms):
   - 同一 IP,每分钟最多 2 次,每小时最多 10 次
   - 注意:Nginx 的 limit_req_zone 可以同时应用多个 zone 吗?
   
3. 普通 API(/api/ 其他路径):
   - 同一 IP,每秒最多 20 次
   - 允许 burst=10(削峰,但超过 burst 立刻拒绝)
   
4. 公开接口(/health, /public/):
   - 不限速

配置要求:
- 所有超速响应返回 429(不是默认的 503)
- 在响应头中包含 X-RateLimit-Limit 和 Retry-After(Nginx 原生支持吗?如果不支持告诉我)
- 每个 limit_req_zone 的内存大小合理计算(10m 可以存多少个 IP?)

基于 Nginx 1.30.2。

提示词 P02:基于用户 ID 的限速(已登录用户)

使用时机:对已认证用户按 user_id 限速,而不是按 IP

比默认多了什么

  • 从 JWT/Session 提取用户标识
  • 未认证用户回退到 IP 限速
帮我配置基于用户 ID 的 Nginx 限速,而不是 IP。

场景:API 网关在 JWT Token 的 Header 里(Authorization: Bearer <token>)。我想按用户 ID 限速,防止单个用户滥用 API(而不是按 IP,因为企业用户可能共享 IP)。

方案分析:
1. Nginx 原生不能解析 JWT,能从 Header 里提取 user_id 吗?
2. 我能用哪个 Nginx 变量作为限速 key?
   - 方案A:用 Authorization Header 的 hash 值(不精确,但不需要解析)
   - 方案B:后端在响应里加 X-User-ID 头,Nginx 缓存并用于限速(复杂但精确)
   - 方案C:在 Nginx 前面加一层 OpenResty/Lua 解析 JWT(需要额外组件)
3. 对于未携带 token 的请求,回退到 IP 限速

告诉我哪个方案最实际可行,给出对应的配置。

基于 Nginx 1.30.2(开源版,不含 Lua)。

提示词 P03:验证限速配置是否真的生效

使用时机:配置完限速后,需要验证配置是否按预期工作

比默认多了什么

  • 压测验证
  • 查看 Nginx 内部计数器
  • 常见失效原因检查
帮我写验证 Nginx 限速配置是否正确生效的测试方法。

我配置的规则:每个 IP 每秒最多 10 个请求,burst=5 nodelay

验证需要:
1. 用 ab(Apache Benchmark)或 hey 工具发送突发请求,验证:
   - 前 10 个在1秒内处理
   - 第 11-15 个(burst 内):立刻处理(nodelay)
   - 第 16+ 个:立刻返回 429
   - 给我具体的 ab 命令

2. 用 curl 循环测试(更直观):
   - 1秒内发10次,全部 200
   - 1秒内发20次,后10次应该 429
   - 给我 Bash 脚本

3. 在 Nginx 日志中找限速触发的记录:
   - 限速触发时 Nginx 日志的格式
   - grep 命令找出所有被限速的请求

4. 检查限速没有生效的常见原因:
   - 配置文件重载了吗(nginx -s reload)?
   - 配置在 server 还是 location 级别(哪个生效)?
   - burst 和 nodelay 是否同时配置了?
   - zone 内存大小是否足够(10m 不够会自动驱逐)

基于 Nginx 1.30.2。

3.4 验收清单

检查项 验证方法 AI辅助
限速实际生效(非排队) 1秒内超量请求收到 429 用 P03 提示词写验证脚本
限速状态码是 429 超速请求响应码是 429,不是 503 让 AI 添加 limit_req_status 429
不同接口有不同规则 登录接口更严格 用 P01 提示词配置分级规则
limit_req_zone 在 http 块 nginx -t 无错误 让 AI 检查配置位置
burst 和 nodelay 正确配置 代码审查 burst/nodelay 参数 让 AI 解释当前配置行为
限速日志可查 grep "limiting" /var/log/nginx/error.log 让 AI 调整日志级别

3.5 本章小结

如果你只记一件事:为登录接口的 limit_req 添加 nodelay 参数。没有 nodelay,你的"限速"实际上是"排队",攻击者的请求最终都能通过。

Nginx 限速的三个关键决策

  1. burst + nodelay:保护关键接口(登录、短信)用 nodelay;普通 API 可以不加(让峰值请求排队处理)
  2. 分接口分规则:登录接口每分钟5次,普通 API 每秒20次——一刀切的规则要么太严(影响正常用户)要么太宽(无法防攻击)
  3. 429 而不是 503:客户端需要知道是被限速了,而不是服务器崩溃了

→ 第4章:AI帮我配置了负载均衡,但健康检查在哪?