nginx 1.11.3+ 必须使用 ngx_http_geoip2_module + geolite2-country.mmdb,旧 geoip_country 指令已失效;需通过 nginx -v 确认模块存在,否则安装对应系统包;数据库路径、权限、geoip2 指令位置(http 块顶层)、set_real_ip_from(处理 cdn/lb)及 map 定义位置(http 块顶层)均需严格配置,否则地域判断失效。

直接说结论:Nginx 1.11.3+ 必须用 ngx_http_geoip2_module + GeoLite2-Country.mmdb,旧版 geoip_country 指令已失效,硬写会报 unknown directive "geoip_country"。
确认 Nginx 是否支持 geoip2 模块
别猜,直接查:
- 运行
nginx -V 2>&1 | grep -o ngx_http_geoip2_module,有输出才说明模块已编译进二进制 - 若无输出,
apt install libnginx-mod-http-geoip2(Ubuntu/Debian)或yum install nginx-mod-http-geoip2(CentOS/RHEL 8+)是最快路径;源码编译需额外加--add-dynamic-module=... - 注意:预装的
nginx-light或nginx-core包几乎都不含该模块,nginx-full或nginx-extras才可能带
加载 GeoLite2 数据库并提取国家代码
数据库必须放对位置、读对权限、配对变量名:
- 下载
GeoLite2-Country.mmdb到如/usr/share/GeoIP/GeoLite2-Country.mmdb,确保nginxworker 进程可读(chown root:www-data+chmod 644) -
geoip2指令必须写在http块顶层,不能嵌套在server内:geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip2_data_country_code source=$remote_addr country iso_code; } - 变量名
$geoip2_data_country_code是自定义的,但必须和后续map中引用的一致;source=$remote_addr表示查谁的 IP —— 如果走 CDN,这一步会错,见下一条
处理代理链与真实客户端 IP
90% 的配置不生效,问题出在这里:$remote_addr 是 CDN 或 LB 的 IP,不是用户真实 IP。
- 先在
http块顶部加可信代理声明:set_real_ip_from 192.168.0.0/16; set_real_ip_from 203.0.113.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on;
(把网段替换成你实际的 CDN/LB 地址段) - 加完后,
$remote_addr才真正代表用户 IP,geoip2查询才有意义 - 验证方法:临时加一行
add_header X-Real-IP $remote_addr;,用curl -H "X-Forwarded-For: 8.8.8.8" http://yoursite测试响应头是否为8.8.8.8
用 map + if 实现安全拦截
别在 location 里写 if ($geoip2_data_country_code = "US") { deny all; } —— deny 在非 http_access 上下文无效,且 if 嵌套层级深易出错。
-
map必须定义在http块顶层(不能在server内),用于预计算布尔值:map $geoip2_data_country_code $allowed_country { default 0; CN 1; JP 1; KR 1; } - 然后在
server块中用if+return(这是 Nginx 官方认可的安全用法):if ($allowed_country = 0) { return 403 "Access denied: your region is not allowed."; } - 注意:
map的default值决定“未匹配国家”的行为,比如设成1就是默认放行,0就是默认拒绝
最常被忽略的是 set_real_ip_from 配置 —— 没它,所有地域判断都基于代理 IP,结果全是错的。数据库路径权限、map 作用域、变量名拼写,三者任一出错都会让 $geoip2_data_country_code 为空,导致 map 走 default 分支,看似“生效”实则逻辑反了。











