Nginx可通过geoip2模块+map指令实现纯原生地理就近分发:先用set_real_ip_from还原真实IP,再加载GeoLite2数据库提取国家/省份码,最后映射至本地路径或upstream集群,default兜底确保高可用。

前提:正确获取并解析真实客户端 IP
如果用户经过 CDN、WAF 或负载均衡器,$remote_addr 就是代理内网地址,GeoIP 查询会失效。必须提前还原真实出口 IP:
- 在 http 块顶部声明可信上游网段,例如:
set_real_ip_from 103.21.244.0/22;(填你实际使用的 CDN CIDR) - 指定原始 IP 存放的请求头:
real_ip_header X-Forwarded-For; - 启用递归解析避免多层代理污染:
real_ip_recursive on; - 加一个调试 location 验证是否生效:
location /ip { return 200 "$realip_remote_addr\n"; },访问确认返回的是公网 IP
关键:加载 GeoIP2 数据库并定义地理变量
使用 ngx_http_geoip2_module(推荐)配合 MaxMind 的 GeoLite2-City.mmdb 数据库,提取国家码、省份码等字段:
- 数据库需定期更新(可用
geoipupdate自动拉取),路径如:/usr/share/GeoIP/GeoLite2-City.mmdb - 在 http 块中加载并定义变量:
geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {
$geoip2_data_country_code source=$remote_addr country iso_code;
$geoip2_data_province_code source=$remote_addr subdivisions 0 iso_code;
}
这样就得到两个可用变量:$geoip2_data_country_code(如 CN、US)、$geoip2_data_province_code(如 GD、BJ、CA)
路由:用 map 指令映射地域到目标路径或 upstream
map 是高效、安全的变量预计算方式,比 if 更适合做地域分流:
- 若分发静态文件(如公告页、地区定制 JS),可映射为本地路径:
map "$geoip2_data_country_code" $region_root {
default "/usr/local/nginx/html/global";
CN "/usr/local/nginx/html/cn";
JP "/usr/local/nginx/html/jp";
}
server {
location /notice/ {
root $region_root;
try_files $uri $uri/ =404;
}
}
- 若代理到不同机房的静态服务集群,映射为 upstream 名称:
upstream backend_cn_gd { server 192.168.10.101:80; }
upstream backend_us_ca { server 10.1.1.101:80; }
map "$geoip2_data_country_code:$geoip2_data_province_code" $upstream_group {
default backend_global;
"CN:GD" backend_cn_gd;
"US:CA" backend_us_ca;
}
location /static/ {
proxy_pass http://$upstream_group;
proxy_set_header Host $host;
}
兜底与运维要点
生产环境不能只靠地域判断,必须考虑异常情况:
- 降级策略:所有 map 的 default 分支必须指向可用后备节点(如全球通用资源池或默认机房)
- 内容同步:各地域节点的静态文件需通过 rsync、inotify 或对象存储同步机制保持一致
-
响应头调试:临时添加
add_header X-Geo-Country $geoip2_data_country_code;方便前端或抓包验证分流是否生效 -
权限与路径安全:避免变量拼接路径导致目录穿越,建议用固定前缀 + 变量(如
/var/www/$country_code/)并限制 root 目录范围


















