多级代理下 limit_req 准确性取决于 KEY 来源、状态同步与配置可信链:需用 $realip_remote_addr(配 set_real_ip_from 等)替代 $remote_addr,确保单机共享内存内限流一致,并通过日志、压测和响应头验证。

在多级代理(如 CDN → Nginx 边缘层 → Nginx 接入层 → 后端)环境中测试 limit_req 的准确性,关键不是“能不能限”,而是“对谁限、按什么 KEY 限、状态是否同步”——因为每层代理都可能改写或丢失原始客户端信息,导致限流对象错位、计数不一致。
确认限流 KEY 的真实来源
限流是否准确,第一步取决于 limit_req_zone 中的 KEY 是否真正代表你要约束的实体。常见误区:
- 用
$remote_addr:在多级代理下它通常是上一跳代理 IP(如 CDN 节点),而非真实用户,会导致全站共用一个桶 - 忽略
X-Forwarded-For头的可信性:若未配置set_real_ip_from,Nginx 不会信任该头,$realip_remote_addr仍为代理 IP
✅ 正确做法:
- 在最外层可信代理(如 CDN 或 LB)后第一台 Nginx 上配置:
set_real_ip_from 192.168.10.0/24;(填你上游代理网段)real_ip_header X-Forwarded-For;real_ip_recursive on; - 限流 KEY 改用
$realip_remote_addr(已还原为真实客户端 IP)或自定义可信头(如$http_x_real_ip)
验证共享内存状态是否跨 worker 一致
limit_req_zone 依赖共享内存(zone=xxx:10m),但该区域仅在单机内有效。多级代理本身不共享状态,所以“准确性”只在单节点维度成立。需确认:
- 同一客户端请求是否稳定落到同一台 Nginx 实例?若前端是轮询负载均衡且无 session stickiness,请求分散到不同机器,限流就变成“每台各自计数”,整体不收敛
- 用
stub_status或nginx-module-vts查看当前 zone 的 active keys 数量和 hit/miss 统计,确认计数在增长 - 临时加日志:在
log_format中加入$limit变量(记录是否被限流)和$binary_remote_addr/$realip_remote_addr,比对日志中同一 IP 的限流触发节奏是否符合rate+burst预期
用可控工具模拟并观察响应行为
避免用浏览器或 curl 手动点,需用并发可控的压测工具验证漏桶节奏:
- 用
ab -n 50 -c 10 http://your-api/(50 请求、10 并发)测试 burst=5 rate=2r/s 场景:应看到约前 5 个快速返回(进桶),后续请求按每 500ms 一个匀速放行(漏出),或超 burst 后直接 429(若配了nodelay) - 在响应头中加
add_header X-RateLimit-Remaining $limit_rate;(需配合 Lua 或 OpenResty)或简单用add_header X-Limit-Hit "$limit";输出限流状态(空值表示未触发,非空如 “-1” 表示被限) - 重点检查 HTTP 状态码:默认是 503,建议显式设
limit_req_status 429;,再用grep " 429 " access.log统计实际拦截数,对比理论值
排除代理层干扰的典型配置片段
以下是在边缘 Nginx 层(已接 CDN)安全启用限流的最小可靠配置:
http {
set_real_ip_from 203.208.60.0/24; # 示例 CDN 网段
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
limit_req_zone $realip_remote_addr zone=perip:10m rate=3r/s;
log_format limitlog '$remote_addr $realip_remote_addr [$time_local] "$request" $status $limit';
}
server {
location = /api/v1/submit {
limit_req zone=perip burst=6 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}
此时压测时,$realip_remote_addr 应始终为真实用户 IP,access.log 中 $limit 字段会在触发限流时出现非空值,且 429 响应数应与漏桶模型严格吻合。


















