limit_req限流失效主因是shared memory zone满,导致令牌桶无法维护;需检查错误日志中“zone is full”或excess:0.000、确认key是否过散(如用$remote_addr)、增大zone大小并改用$binary_remote_addr。

当 Nginx 的 limit_req 限流失效,且日志中频繁出现 limiting requests, excess: X.XX0 by zone "xxx" 或完全不生效(如大量请求未被拒绝),很可能是 limit_req_zone 定义的共享内存区域(shared memory zone)已满,导致令牌桶无法正常维护,进而使限流逻辑退化或失效。
确认 shared memory zone 是否已满
Nginx 不会直接报“zone full”,但可通过以下方式验证:
- 检查 Nginx 错误日志(
error_log级别为warn或更高):若看到类似could not add new limit_req entry, zone is full(实际日志取决于版本,1.21+ 更明确)或反复出现excess: 0.000(表示本该限流却无余量可扣,可能因桶元数据丢失),说明 zone 已达容量上限; - 使用
nginx -T | grep limit_req_zone查看当前 zone 配置,重点关注zone=name:size中的size值(如zone=api:10m); - 通过
nginx -V 2>&1 | grep -o 'with-http-limit-req-module'确认模块已启用,并用ps aux | grep nginx查看 worker 进程是否运行正常(避免因 worker 崩溃导致限流旁路)。
理解 zone 内存占用的真实开销
共享内存 zone 并非按请求数线性分配,而是为每个**唯一 key**(如 $binary_remote_addr)分配固定大小的槽位(slot),用于存储令牌桶状态。关键点:
- 一个 key 占用约 64 字节(Nginx 1.19+ 后更紧凑,但仍与 key 数强相关);
- 若 key 来源是
$remote_addr(含端口)或$http_x_forwarded_for(易被伪造、含多 IP),会导致 key 数暴增,快速耗尽 zone; - 例如:10MB zone 最多容纳约 16 万 key(10×1024×1024 ÷ 64 ≈ 163840),若单个 IP 被拆成多个 key(如代理透传不一致),极易打满。
快速定位和修复配置问题
立即生效的排查与调整建议:
-
收紧 key 定义:优先用
$binary_remote_addr(IPv4 固定 4 字节 + IPv6 16 字节,哈希后统一为 32 字节),避免$remote_addr(含端口)或不可信 header; -
增大 zone 大小:根据预估并发独立客户端数扩容,如从
10m改为32m或64m,注意 32 位系统单 zone 不宜超 512m; -
检查 proxy 设置:若前端有 LB/CDN,确保
real_ip_header和set_real_ip_from正确配置,使$binary_remote_addr真实反映客户端,否则所有请求可能被识别为同一个 key 或大量无效 key; -
临时验证:加一条测试规则,如
limit_req_zone $binary_remote_addr zone=test:1m rate=1r/s;,并用ab -n 100 -c 10 http://host/观察是否触发限流及错误日志变化。
长期可观测性建议
避免下次被动排查:
- 在
server或location中添加limit_req_status 503;并配合自定义日志格式,记录被限流请求的 key 和时间; - 用
stub_status模块(需编译时启用)或第三方模块如nginx-module-vts监控 zone 使用率; - 对高动态 key 场景(如 API Key 限流),考虑用外部服务(Redis + lua-resty-limit-traffic)替代内置 zone,规避内存瓶颈。











