limit_req_zone必须写在http块中,仅声明共享内存区域,需配合limit_req在server或location中启用;错误写入server/location会报错或静默失效。

limit_req_zone 必须写在 http 块里,否则配置不生效
很多人在 server 或 location 块里直接写 limit_req_zone,Nginx 启动会报错:invalid number of arguments in "limit_req_zone" directive,或者静默忽略。这个指令只能出现在 http 上下文,且必须在 server 之前定义。
它本质是声明一个共享内存区域(zone),所有 worker 进程共用。常见错误包括:
- 把
$remote_addr当成键用 → 内存占用翻倍,应改用$binary_remote_addr - zone 大小设太小(如
1m)→ 高并发时 key 溢出,新 IP 无法建状态,限流失效 - rate 写成
10r/m却没注意实际是每分钟 10 个 → 等效于约 0.17r/s,防护形同虚设
burst 和 nodelay 组合决定请求是排队还是秒拒
burst 不是“并发上限”,而是漏桶的容量;nodelay 控制是否跳过排队直接处理或拒绝。两者搭配效果截然不同:
-
limit_req zone=byip burst=20;→ 超速请求会排队,Nginx 尝试按 rate 匀速放出,可能造成响应延迟升高 -
limit_req zone=byip burst=20 nodelay;→ burst 内的请求立刻放行,超出的直接返回503(或自定义状态码) -
burst=0且无nodelay→ 所有超速请求一律延迟,实际中极少用,容易拖垮后端
防 CC 场景下,nodelay 是更安全的选择:宁可快速失败,也不让恶意请求长期占着连接。
按 IP + URI 组合限流能防“扫接口”但要注意内存开销
单靠 $binary_remote_addr 容易被代理池绕过;只按 $uri 又无法区分真实用户。组合键如 "$binary_remote_addr$uri" 更精准,但代价明显:
- 每个键长度变长 → 同样 10MB zone,可存的键数量下降约 30%~40%
- 高频变动的 URI(如带时间戳、随机参数)会导致键爆炸,迅速填满 zone
- 建议对关键路径做白名单过滤,例如只对
/login、/api/order生效,避免全量匹配
示例配置中 limit_req_zone "$binary_remote_addr$uri" zone=ipuri:10m rate=3r/s; 看似严密,若没加 location 范围控制,可能把健康心跳请求也干掉。
log_format 里必须加 $limit_req_status 才能看清谁被拦了
默认 access log 完全看不出哪个请求是因为限流被拦的。不加 $limit_req_status,你只能看到一堆 503,但分不清是后端崩了,还是规则真起作用了。
正确做法是在 log_format 中显式包含它:
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$limit_req_status"';
日志中会出现 passed、rejected、delayed 三种值。特别注意:rejected 对应 nodelay 触发的秒拒,delayed 表示请求进了队列但还没轮到——后者在高负载时可能堆积数秒,反而放大攻击效果。
真正难调的不是配出来,而是配出来之后怎么验证它没误杀、也没漏防。监控 $limit_req_status 的分布比例,比盯着 QPS 曲线有用得多。


















