必须使用geoip2模块而非geo模块才能实现全球IP精准归国;需加载GeoLite2-Country.mmdb数据库,通过map定义国家码黑名单,并配合real_ip配置确保真实IP识别。

直接用 geo 模块无法实现“全球 IP 地址精准归类到国家”,它只支持手动配置 IP 段或 CIDR 范围,不具备自动解析 IP 所属国家的能力。真正能完成这项任务的是 geoip2 模块(非旧版 geo 或已废弃的 http_geoip_module)。下面分三步讲清楚怎么做:
确认用对模块:必须是 geoip2,不是 geo
Geo 模块本质是静态 IP 区间映射工具,比如写死 192.168.1.0/24 internal;,它不连数据库、不查 MaxMind、不识别国家码。而 geoip2 模块加载 GeoLite2-Country.mmdb 后,才能把任意 IPv4/IPv6 地址实时映射为 $geoip2_data_country_code(如 CN、US、RU)。
验证是否启用成功:在 http 块加一个测试 location:
location /geo-test { return 200 "$geoip2_data_country_code\n"; }用 curl http://your-site/geo-test 返回两位国家码,说明模块工作正常。
在 http 块定义国家码变量与拦截逻辑
所有地理判断必须提前到 http 或 server 级别,避免在 location 中滥用 if。推荐用 map 构建可复用的拦截开关:
- 先加载数据库(路径按实际调整):
geoip2 /www/geoip/GeoLite2-Country.mmdb {
$geoip2_data_country_code country iso_code;
}- 再用 map 定义黑名单状态:
map $geoip2_data_country_code $block_flag {
default 0;
"KP" 1; # 朝鲜
"SY" 1; # 叙利亚
"IQ" 1; # 伊拉克
}这个 $block_flag 是布尔型变量,值为 1 时代表需拦截。
在 location 中干净执行阻断
把拦截动作放在具体 location 块里,用最简方式返回 403:
location / {
if ($block_flag) {
return 403 "Access denied from your region.";
}
# 后续正常 proxy_pass 或 try_files
}注意:if 在 location 内用于简单变量判断是安全且低开销的,Nginx 官方也未禁止此类用法;但不要在 location 里做复杂逻辑或嵌套条件。
如果要拦截全部路径(包括 API、静态资源、后台入口),建议放在 server 块顶层或使用 rewrite 配合 return 统一处理。
特别注意真实 IP 问题
若 Nginx 前有 CDN 或负载均衡器,$remote_addr 是代理 IP,会导致国家识别全错。必须配置:
-
set_real_ip_from声明可信代理网段(如 Cloudflare、阿里云 SLB 的 IP 段) -
real_ip_header X-Forwarded-For或X-Real-IP(依你前端实际 header 名) - 确保
geoip2指令中source=$remote_addr改为source=$realip_remote_addr(部分版本需显式指定)

















