Nginx 本身不支持 IP 地理位置识别,需借助 ngx_http_geoip2_module 模块与 GeoLite2 数据库实现国家分流,再通过 map 预定义路由、include 分离配置,或 Lua 动态匹配 CIDR;同时须定期更新 IP 库并确保 $remote_addr 准确还原。

Nginx 本身不内置 IP 地理位置识别功能,不能直接通过 include 加载“国家 IP 段列表”来实现地理分流。但可以结合第三方模块(如 ngx_http_geoip2_module)和外部 IP 归属数据库(如 MaxMind GeoLite2),再配合 include 管理配置片段,实现按国家分流。核心在于:先识别国家,再用 if 或 map 做路由决策,include 仅用于组织配置文件。
1. 准备 GeoLite2 数据库和 ngx_http_geoip2_module
这是基础前提。Nginx 默认不带 GeoIP2 支持,需编译时启用该模块(或使用已集成该模块的发行版,如 Ubuntu 的 nginx-extras):
- 下载 GeoLite2 Country CSV 或 MMDB 格式数据库(推荐 MMDB,性能更好),放在如
/usr/share/GeoIP/GeoLite2-Country.mmdb - 确认 Nginx 已加载模块:
nginx -V 2>&1 | grep -o with-http-geoip2-module;若无,需重新编译或换包 - 在
http块中加载数据库:geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {<br> $geoip2_data_country_code country iso_code;<br>}
2. 用 map 提前映射国家代码到上游或路径
避免在 server 或 location 中频繁调用 if,推荐用 map 预定义分流逻辑,再用 include 引入规则文件:
- 新建
/etc/nginx/conf.d/country-routes.conf,内容示例:map $geoip2_data_country_code $upstream_backend {<br> default backend_default;<br> CN backend_cn;<br> US backend_us;<br> JP backend_jp;<br>} - 在主配置中
include /etc/nginx/conf.d/country-routes.conf; - 后续在
location中直接引用:proxy_pass http://$upstream_backend;
3. 用 include 分离不同国家的 location 或 rewrite 规则
当各国处理逻辑差异较大(如静态资源路径、重写规则、限速策略),可为每国建独立配置文件,用 include 动态加载:
- 创建
/etc/nginx/conf.d/country/目录,内含:cn.conf(含针对中国的缓存头、CDN 回源设置)us.conf(含针对美国的 CORS 头、日志标记) - 在 server 块中按条件引入:
if ($geoip2_data_country_code = "CN") {<br> include /etc/nginx/conf.d/country/cn.conf;<br>}<br>if ($geoip2_data_country_code = "US") {<br> include /etc/nginx/conf.d/country/us.conf;<br>} - 注意:
if在 location 外可用,但在 location 内慎用;更稳妥的方式是用map+ 多个location块配合变量匹配
4. 替代方案:用 Lua + OpenResty 实现动态 IP 段匹配
如果无法使用 GeoIP2(如受限于环境),且已有各国 CIDR 列表(如从 ipgeolocation.io 或 ip2location 下载),可用 lua-resty-iputils 在运行时判断:
- 安装 OpenResty,启用 Lua 模块
- 将各国 CIDR 存为 JSON 文件(如
/etc/nginx/lua/countries.json),用include不适用,改用ngx.timer.every或启动时加载到 shared dict - 在
access_by_lua_block中遍历 CIDR 判断归属,设置$country_code变量,再交由map或if路由 - 此方式灵活但性能略低,适合 CIDR 总数不超几千条的场景
不复杂但容易忽略:IP 库需定期更新(MaxMind 每月发布新版本),且真实用户可能使用代理或 CDN,原始 IP 可能是节点地址而非真实地理位置——务必检查 $remote_addr 是否已被 real_ip 指令正确还原。


















