CDN转发后Nginx限流失效的核心原因是$remote_addr被替换为CDN节点IP而非真实用户IP,导致limit_req_zone对少数节点限流;需通过set_real_ip_from、real_ip_header和real_ip_recursive配置使$binary_remote_addr还原为真实IP,并确保限流key基于该变量,同时排除CDN自身限流干扰。

CDN 转发后 Nginx 限流失效,核心问题是:Nginx 看到的客户端 IP 变成了 CDN 节点的 IP(如 Cloudflare、阿里云 CDN 的出口 IP),而不是真实用户 IP。于是 limit_req_zone $binary_remote_addr 实际上是对几个 CDN 节点做限流,导致所有用户共享极宽松的额度,形同虚设。
确认是否被 CDN 代理覆盖了真实 IP
先查 Nginx access log,看 $remote_addr 字段记录的是不是一串固定 IP(比如全是 104.28.x.x 或 120.236.x.x)。如果是,说明没透传真实 IP。再检查请求头里是否有 X-Forwarded-For 或 X-Real-IP —— 主流 CDN 都会带这两个头,但 Nginx 默认不读取它们。
验证方法:临时加一行日志格式,输出真实头信息:
log_format debug '$remote_addr - $http_x_forwarded_for - $http_x_real_ip - "$request" $status';
重启后看日志,若 $http_x_forwarded_for 显示多个 IP(如 203.12.45.67, 104.28.1.2),最左边才是用户真实 IP。
修复 IP 识别:用 set_real_ip_from + real_ip_header
在 http 块中添加 CDN 官方公布的可信出口网段(务必核对最新文档,例如 Cloudflare 全列表约 30+ 段):
-
Cloudflare:
set_real_ip_from 173.245.48.0/20;等共 12 条,需全部写上 -
阿里云 CDN:
set_real_ip_from 110.75.0.0/16;等约 10 段(参考控制台「回源配置 → CDN 节点 IP 段」) - 然后统一指定头字段:
real_ip_header X-Forwarded-For;或更安全的real_ip_header X-Real-IP; - 最后加
real_ip_recursive on;支持多层代理(如 Nginx 前还有 WAF)
完成后再用 $binary_remote_addr 就能拿到真实用户 IP,限流才真正按人计数。
检查限流 key 是否仍依赖错误变量
即使修复了 IP 识别,如果限流配置还写的是 $remote_addr(未更新为识别后的地址),依然无效。必须确保:
-
limit_req_zone中的 key 使用$binary_remote_addr(它已由 real_ip 模块重写) - 不要用
$http_x_forwarded_for直接做 key——它可被伪造,且含逗号分隔多 IP,易出错 - 若需按域名或 URI 限流,可组合使用:
limit_req_zone $binary_remote_addr$host zone=host_limit:10m rate=20r/s;
验证限流是否真正生效
别只看返回状态码,要交叉验证:
- 用 curl 加 CDN 头模拟真实请求:
curl -H "X-Forwarded-For: 192.168.1.100" http://yoursite.com/api/test - 观察 access log 中该 IP 的请求频率是否被拦截(出现
503或505) - 查 Nginx error log,搜索
limiting requests,有日志即表示模块已触发 - 注意 CDN 自身可能也有限流(如 Cloudflare 的 Rate Limiting 规则),需关闭或协调策略,避免双重限制冲突


















