Nginx 的 ip_hash 不支持按地区路由,仅对客户端 IP 做固定哈希(IPv4 取前三段,IPv6 用完整地址),无地理信息解析能力;需借助 geoip2 模块获取地区码,再通过 hash $geoip2_country_code consistent 实现地区级稳定路由。

Nginx 的 ip_hash 不支持基于地区代码(如国家、省份、城市)的哈希,它只对客户端原始 IP 地址做固定哈希运算:IPv4 取前三段(如 192.168.1.x → 视为 192.168.1),IPv6 使用完整地址(需 Nginx ≥1.19)。它没有内置地理信息解析能力,也无法直接读取或判断 IP 所属地区。
要实现“按地区代码路由”,必须跳出 ip_hash 的限制,采用组合方案:先识别地区,再通过其他机制实现定向分发。
如何按客户端地区做稳定路由?
你需要两步:
✅ 第一步:获取真实客户端 IP 并查出其地区(国家/省/市)
✅ 第二步:用该地区标识作为路由依据(不能用 ip_hash,得换方式)
获取地区信息
Nginx 本身不带 IP 归属库,需借助外部模块或上游服务:
-
推荐方案:使用
ngx_http_geoip2_module(主流生产选择)
安装后加载 MaxMind GeoLite2 数据库,可提取$geoip2_data_country_code、$geoip2_data_subdivision_1_name等变量。示例配置片段:
http { # 加载 GeoLite2 国家库 geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip2_country_code source=$remote_addr country iso_code; } # 加载 GeoLite2 城市级库(可选) geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb { $geoip2_province source=$remote_addr subdivisions 0 names en; } upstream backend_cn { server 10.0.1.10:8080; } upstream backend_us { server 10.0.1.20:8080; } map $geoip2_country_code $backend_group { default backend_us; CN backend_cn; JP backend_cn; # 日本也走中国集群 } server { location / { proxy_pass http://$backend_group; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
⚠️ 注意:
map+proxy_pass http://$variable是动态上游路由,不是会话保持。如需“同一地区用户始终落在同一台后端”,还需叠加hash指令(见下文)。
实现“地区内会话保持”的替代方案
既然 ip_hash 不适用,可用 hash 指令自定义键:
upstream backend_region {
hash $geoip2_country_code consistent; # 一致性哈希,抗节点变动
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
location / {
proxy_pass http://backend_region;
# 确保 geoip2 已生效,且 $remote_addr 是真实 IP
}
}-
hash $geoip2_country_code consistent:所有 CN 用户请求哈希到固定后端,且扩容时漂移最小 - 必须配合
geoip2正确识别地区,否则$geoip2_country_code为空,导致哈希结果不可控
替代轻量方案(无 GeoIP 模块时)
若无法编译安装 geoip2,可由前端或上游网关(如 CDN/WAF)注入地区头:
# 假设上游已设置 X-Client-Region: CN / US / GD(广东省)
map $http_x_client_region $backend_pool {
CN backend_cn;
US backend_us;
GD backend_gd;
default backend_default;
}
upstream backend_cn {
hash $http_x_client_region consistent;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}并在 location 中透传该头:
proxy_set_header X-Client-Region $http_x_client_region;
为什么不能用 ip_hash 实现地区路由?
-
ip_hash是硬编码逻辑,只认 IP 字节,不查库、不调 API、不支持变量 - 同一地区用户 IP 分散(如北京有上万公网出口 IP),哈希结果天然分散,无法聚类
- 不同地区用户可能共享 IP(NAT、校园网),反而导致误归类
- 无法应对 IPv6、代理链、CDN 回源等复杂网络场景
验证是否生效
在 access log 中加入地区与上游信息,便于排查:
log_format region_log '$remote_addr [$time_local] $geoip2_country_code "$upstream_addr" $status'; access_log /var/log/nginx/region.log region_log;
访问后检查日志,确认:
-
$geoip2_country_code是否有值(如CN) - 同一地区 IP 的请求是否长期落在相同
$upstream_addr
不复杂但容易忽略:地区路由的关键不在哈希指令本身,而在于真实、稳定、低延迟的地区识别链路。选对数据源和传递方式,比纠结 ip_hash 写法重要得多。


















