单纯靠nginx原生geo模块无法识别国家或地区,必须使用ngx_http_geoip2_module加载geolite2数据库,通过geoip2指令提取国家/城市代码,再结合map实现黑白名单、if拦截、正则匹配城市id或limit_req按国别限流。

单纯靠 Nginx 自带的 geo 模块无法识别国家或地区,它只支持静态 IP 段匹配(如内网、IDC 段),精度停留在粗粒度区域划分。要实现“按国家或地区精准拦截”,必须用 ngx_http_geoip2_module 加载 MaxMind GeoLite2 数据库,再结合 map 和访问控制指令完成策略落地。
用 geoip2 提取真实国家/城市代码
这是整个流程的前提。原生 geo 模块不查库、不识别地理位置,真正做国家判断的是 geoip2 指令:
- 确认模块已启用:
nginx -V 2>&1 | grep -o with-http-geoip2-module,无输出需重新编译并添加--add-module - 下载免费数据库
GeoLite2-Country.mmdb(推荐放至/usr/share/GeoIP/) - 在
http块中加载并定义变量:
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
$geoip2_country_code source=$remote_addr country iso_code;
} - 若前端有 CDN 或代理,务必配合
real_ip配置信任头(如set_real_ip_from 103.21.244.0/22;),否则取到的是代理 IP
用 map 实现国家级黑白名单
map 是轻量、高效、可 reload 的映射方式,适合长期固定的国家级策略:
- 定义允许访问的国家:
map $geoip2_country_code $allowed {
default 0;
CN 1;
JP 1;
KR 1;
} - 在
location中拦截非白名单请求:
location /api/ {
if ($allowed = 0) { return 403; }
proxy_pass http://backend;
} - 也可反向做黑名单(如屏蔽 RU、KP、IR):
map中设这些国家为 1,if ($blocked = 1)返回 403
进阶:按城市或省份做更细粒度拦截
当国家粒度不够用时(例如只开放北上广深、限制某省高频请求),切换到 GeoLite2-City.mmdb:
- 提取城市 ID(比英文名更稳定):
geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {
$geoip2_city_geoname_id source=$remote_addr city geoname_id;
} - 用正则匹配多个城市 ID:
if ($geoip2_city_geoname_id !~ "(2643743|2643741|1819730|1792953)") { return 403; }
(对应 London、New York、Beijing、Shenzhen) - 如需中文城市名,需指定
names zh-CN并确保 Nginx 支持 UTF-8 字符比较
柔性控制:按国家限流代替硬拦截
直接 return 403 容易误伤,对 API 类接口建议用 limit_req 实现差异化限流:
- 定义以国家码为 key 的限流区:
limit_req_zone $geoip2_country_code zone=by_country:10m rate=3r/s; - 在目标 location 启用:
location /api/pay {
limit_req zone=by_country burst=5 nodelay;
proxy_pass http://pay_backend;
} - 效果:每个国家独立配额,CN 用户每秒最多 3 次,US 用户也各自独立 3 次,互不影响











