nginx geo模块仅支持静态ip段匹配,无法识别国家/城市;需手动整理归属ip段(如cn-isp),用geo定义$region变量,再通过map转换为$is_china等语义值,配合realip模块还原真实ip后实现分流或拦截。

Nginx 的 geo 模块本身不识别地理位置,它只做静态 IP 段匹配,精度停留在网段级别(如 ISP 出口、IDC 区域、内网地址),无法判断国家、省份或城市。所谓“按地理位置控制”,实际是把已知地理归属的 IP 段手动整理后,用 geo 建立映射变量,再配合其他指令实现策略落地。
下面分几种典型场景说明怎么用:
用 geo 定义区域标识变量
在 http 块中使用 geo 指令,为不同 IP 段分配语义化值(比如国家简码、区域类型):
geo $region {
default "unknown";
127.0.0.1 "local";
192.168.0.0/16 "internal";
202.96.0.0/16 "cn-isp-a";
114.255.0.0/16 "cn-isp-b";
8.8.8.0/24 "us-google";
1.1.1.0/24 "au-cloudflare";
}
- 支持 CIDR、IP 范围(
192.168.1.1-192.168.1.255)、单 IP 和include外部文件 - 匹配遵循最长前缀优先原则(
/24比/16优先) - 变量
$region可在后续map、if、日志、proxy_set_header中直接引用
用 map 做语义转换与策略预计算
避免在 location 中频繁用 if 判断字符串,推荐用 map 提前算出布尔标志或行为类型:
map $region $is_china {
default 0;
"cn-isp-a" 1;
"cn-isp-b" 1;
"internal" 1;
}
map $region $backend_group {
default "default";
"cn-isp-a" "cn-beijing";
"cn-isp-b" "cn-shanghai";
"us-google" "us-aws";
}
之后就能安全用于分流或拦截:
-
proxy_pass http://$backend_group; -
if ($is_china = 0) { return 403; }(仅限简单布尔判断)
必须还原真实客户端 IP
如果 Nginx 前有 CDN、WAF 或 SLB,$remote_addr 是代理地址,geo 查的全是错的。需启用 realip 模块并配置:
set_real_ip_from 192.168.0.0/16; # 你信任的代理网段(如阿里云SLB、Cloudflare) set_real_ip_from 2a06:98c0::/29; # IPv6 代理段 real_ip_header X-Forwarded-For; real_ip_recursive on;
这样 $remote_addr 才会变成用户真实 IP,geo 匹配才有效。
geo 的局限与替代建议
- ✅ 适合:内网标识、固定 IDC/IP 段白名单、轻量级区域分流
- ❌ 不适合:按国家/城市精准拦截、动态住宅 IP 场景、需自动更新归属数据
- ? 真正需要国家/地区/城市级控制,请换
ngx_http_geoip2_module+ GeoLite2.mmdb数据库,它能提取$geoip2_country_code、$geoip2_city_name等变量,再配合map或limit_req实现精细策略
不复杂但容易忽略。











