ip_hash本身不支持忽略特定IP,需改用hash指令配合map提取真实IP并校验:先通过map匹配可信CDN网段提取X-Forwarded-For中的用户IP,再标准化清洗,最后在upstream中使用hash $real_client_ip consistent实现精准会话保持。

在 Nginx 中,ip_hash 本身不支持“忽略某些 IP 做哈希”,它会直接对 $remote_addr(即 TCP 连接的真实客户端地址)做哈希。但如果你的流量经过可信 CDN(如 Cloudflare、阿里云全站加速、腾讯云 CDN 等),真实用户 IP 通常放在请求头(如 X-Forwarded-For 或 X-Real-IP)里,而 $remote_addr 是 CDN 节点的 IP —— 直接用 ip_hash 会导致所有用户被散列到同一个后端(因为 CDN 出口 IP 固定)。
核心思路:不用 ip_hash,改用 hash 指令 + 可信 IP 提取
Nginx 的 hash 指令比 ip_hash 更灵活,支持对任意变量(包括自定义变量)做一致性哈希。关键在于:只对真实用户 IP 哈希,且**跳过不可信来源**(即未带可信头或头值不可信时,回退到 $remote_addr 或拒绝)。
具体分三步实现:
-
1. 预先声明可信 CDN 网段,并用 map 提取可信真实 IP
在http块中定义可信 CDN IP 段(务必严格匹配你实际使用的 CDN 出口网段,可从 CDN 厂商文档获取):map $remote_addr $trusted_real_ip { default ""; # Cloudflare 示例(以官方公布的 IPv4/IPv6 网段为准) ~^173\.245\.48\. $http_x_forwarded_for; ~^103\.21\.244\. $http_x_forwarded_for; ~^103\.22\.200\. $http_x_forwarded_for; ~^103\.31\.4\. $http_x_forwarded_for; ~^141\.101\.64\. $http_x_forwarded_for; # 阿里云 CDN 示例(需替换为实际网段) ~^47\.91\.0\. $http_x_real_ip; ~^47\.92\.128\. $http_x_real_ip; # 其他 CDN 网段依此类推... }注意:
map匹配的是$remote_addr(CDN 节点 IP),若匹配成功,则提取对应请求头中的用户 IP;否则设为空字符串。 -
2. 清洗并标准化真实 IP(防伪造)
CDN 头(如X-Forwarded-For)可能含多个 IP(形如"203.0.113.1, 198.51.100.1"),应只取最左边、且来自可信源的第一个 IP:map $trusted_real_ip $real_client_ip { "" $remote_addr; # 未命中可信 CDN,退回到连接源 IP(可选,也可返回空并 deny) ~^(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) $ip; ~^([0-9a-fA-F:]+) $1; # 匹配 IPv6 }</ip>该 map 将
$trusted_real_ip中第一个合法 IPv4/IPv6 地址提取为$real_client_ip;若为空则 fallback 到$remote_addr(生产环境建议改为""并配合后续校验)。 -
3. 在 upstream 中使用 hash 指令替代 ip_hash
在upstream块中启用一致性哈希,目标变量为$real_client_ip:upstream backend { hash $real_client_ip consistent; server 10.0.1.10:8080; server 10.0.1.11:8080; server 10.0.1.12:8080; }加上
consistent参数可提升节点增减时的缓存命中率。此时只有真实用户 IP 参与哈希,CDN 节点 IP 完全不参与计算。
必须配套的安全校验(防 IP 伪造)
仅靠 map 匹配 CDN 网段还不够 —— 攻击者可能伪造 X-Forwarded-For 头。必须确保:只有来自可信 CDN 网段的请求,才信任其携带的用户头。
推荐在 server 或 location 块中添加校验逻辑:
set $real_ip "";
if ($remote_addr ~ "^(173\.245\.48\.|103\.21\.244\.|103\.22\.200\.|103\.31\.4\.|141\.101\.64\.)") {
set $real_ip $http_x_forwarded_for;
}
if ($remote_addr ~ "^(47\.91\.0\.|47\.92\.128\.)") {
set $real_ip $http_x_real_ip;
}
# 若 real_ip 为空或格式非法,拒绝或记录告警
if ($real_ip = "") {
return 403;
}
# 可选:进一步校验 $real_ip 是否为合法公网 IP(非私有地址、非保留地址)
验证与调试技巧
- 开启 Nginx 日志,记录
$remote_addr、$http_x_forwarded_for、$real_client_ip,确认提取逻辑正确:log_format debug '$remote_addr - $http_x_forwarded_for → $real_client_ip'; access_log /var/log/nginx/debug.log debug;
- 用 curl 模拟 CDN 请求:
curl -H "X-Forwarded-For: 203.0.113.5" --local-port 173.245.48.100 http://your-domain.com
观察日志中是否提取出
203.0.113.5;再换一个非 CDN IP 模拟直连,确认 fallback 行为。 - 检查
upstream状态页(需启用stub_status)或用nginx -T输出完整配置,确认hash指令已生效。
本质上,这不是“让 ip_hash 忽略 CDN”,而是放弃 ip_hash,用更可控的 hash + map + 安全校验组合实现相同目标 —— 既保证用户会话粘性,又彻底规避 CDN 节点 IP 干扰。











