Nginx如何识别Cloudflare转发/直连IP并配置匹配限流规则
混合场景下Nginx限流配置的可行性判定与正确实现
两类自行尝试的配置方案说明
1. 按请求头动态切换限流key的条件判断写法完全不可行
limit_req_zone、limit_conn_zone是Nginx http层级的全局初始化指令,在服务启动加载配置时就完成了zone空间分配与key绑定,根本不支持请求处理阶段做if分支动态切换限流规则,这类写法会直接触发配置语法错误,根本无法正常启动服务。
2. 同时配置两个独立限流zone的写法语法合法,但存在严重防护缺陷
- 这种写法本身不会报语法错误,但实际运行时存在两个核心问题:
- 同一个请求会同时触发两个zone的配额计数:所有经Cloudflare转发的请求,
$binary_remote_addr值都是Cloudflare节点的回源IP,意味着来自同一个CF节点的所有用户请求都会被判定为同一个IP,很容易因为普通用户的正常访问占满CF节点的限流配额,造成大面积误杀。 - 防护能力直接减半:当请求被两个独立zone分别计数时,攻击者可以通过不携带XFF头、或者携带随机伪造XFF头的方式,实际拿到双倍的限流配额,规则的防护效果打对折。
- 同一个请求会同时触发两个zone的配额计数:所有经Cloudflare转发的请求,
适配Cloudflare转发+直连混合场景的正确配置逻辑
核心思路是先提取统一的可信客户端IP作为限流key,全程只使用单个限流zone,从根源上避免多zone计数、动态切换的问题:
- 用geo模块标记所有来自Cloudflare的回源请求
匹配请求的底层连接IP$binary_remote_addr,判断当前请求是不是从Cloudflare官方公布的回源IP段发过来的:geo $is_cloudflare_source { default 0; # 填入Cloudflare官方公布的全量回源IP段即可 173.245.48.0/20 1; 103.21.244.0/22 1; } - 用map模块生成统一的可信IP变量
对来自Cloudflare的请求,取CF-Connecting-IP头携带的真实客户端IP作为限流key;对直连请求,直接取底层连接IP$binary_remote_addr作为限流key。这种提取逻辑天然防伪造:直连请求就算刻意伪造CF-Connecting-IP头,因为来源IP不在Cloudflare回源段的标记里,也不会被取用,最终还是会用它本身的连接IP作为限流key:map $is_cloudflare_source $trusted_client_ip { 0 $binary_remote_addr; 1 $http_cf_connecting_ip; } - 基于统一的可信IP变量配置限流规则
不管是CF转发还是直连请求,所有流量都会进入同一个限流zone按真实客户端IP计数,不会出现之前提到的误杀、配额拆分问题:limit_req_zone $trusted_client_ip zone=k_request_limit_per_ip:10m rate=400r/s; limit_conn_zone $trusted_client_ip zone=k_connection_limit_per_ip:10m;
你提到的“直接放行所有Cloudflare IP段、仅对非CF来源IP做限制”的方案存在明确漏洞:如果完全不对CF来源的请求做限流,攻击者完全可以借道Cloudflare的免费节点发起大量恶意请求,直连侧的限流规则对这类流量完全失效,根本起不到防护作用。
内容的提问来源于stack exchange,提问作者Esqarrouth
相关产品推荐
相关产品推荐

