Nginx 本身不识别国家,必须依赖 ngx_http_geoip2 模块与 MaxMind GeoLite2 数据库实现按国封禁;iptables/ufw 等防火墙无法按国家粒度拦截,仅能硬匹配变动频繁的 IP 段,维护成本高且易误杀漏放。

直接说结论:Nginx 本身不识别国家,必须靠 ngx_http_geoip2 模块 + MaxMind GeoLite2 数据库才能按国家封禁;纯防火墙(如 iptables 或云厂商 CFW)做不到按“国家”粒度拦截,只能按 IP 段硬匹配,维护成本极高、极易误杀或漏放。
为什么不能只用 iptables 或 ufw 封国外 IP
Linux 内置防火墙没有地理信息能力。所谓“封国外”,本质是封成千上万个分散的 IP 段,而这些段每天都在变——APNIC、RIPE、ARIN 分配新地址,旧段可能转为国内 CDN 或云服务出口。你手动收集、更新、加载进 iptables 的规则集,不出一周就失效或拖垮性能。
-
iptables -A INPUT -s 1.0.0.0/24 -j DROP这类写法,无法告诉你这个段属于日本还是越南 - MaxMind 官方数据库(如
GeoLite2-Country.mmdb)才是目前事实标准,它把每个 IPv4/IPv6 地址映射到$geoip2_data_country_code字符串(如CN、US) - 云防火墙(如腾讯云 CFW)的“地域控制”功能,底层其实也是调用类似 GeoIP 库,不是原生内核能力
ngx_http_geoip2 模块安装关键点
这个模块不是 Nginx 默认内置的,必须编译进 Nginx 二进制。很多教程跳过版本兼容性,直接导致 nginx -t 报错 unknown directive "geoip2"。
- Nginx 版本必须 ≥ 1.18 ——
nginx -v查看,低于该版本会编译失败或运行时报错 - 依赖必须装全:
libmaxminddb-devel(CentOS/RHEL)或libmaxminddb-dev(Debian/Ubuntu),缺一个,./configure就找不到库 - 模块源码要从
https://github.com/leev/ngx_http_geoip2_module拉取,别用已废弃的ngx_http_geoip_module(只支持老版 GeoIP.dat,无 IPv6) - 编译时务必复用原有 configure 参数:
nginx -V 2>&1 | grep -o 'configure arguments:.*',再把--add-module=/path/to/ngx_http_geoip2_module加进去,否则容易丢掉 SSL、HTTP/2 等关键模块
nginx.conf 中封禁国外 IP 的正确写法
网上大量示例用 if ($geoip2_data_country_code != "CN") { return 403; },这是危险写法:Nginx 的 if 在 location 外使用有隐式限制,且 $geoip2_data_country_code 在未命中数据库时为空字符串,会导致所有请求被误封。
- 先在
http块加载数据库:geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip2_metadata_country_iso_code source=$remote_addr country iso_code; } - 在
server或location块中用map做安全映射:map $geoip2_metadata_country_iso_code $allowed_country { default no; CN yes; } - 再用
if判断(仅限此处):if ($allowed_country = no) { return 403; } - 注意:不要用
rewrite跳转到外部域名(如https://baidu.com),Nginx 会发 302,暴露你的真实后端;return 403或return 444(关闭连接)更干净
数据库更新和权限问题最容易被忽略
GeoLite2 免费库每月底更新,但很多人下载一次就再也不动,半年后数据库里连 TikTok 香港节点的 IP 都没标注,封禁效果归零。
- 下载地址必须用 MaxMind 官方新接口:
https://git.io/GeoLite2-Country.mmdb(需注册账号获取 License Key,免费) - 数据库文件权限必须是 Nginx worker 进程可读:
chown nginx:nginx /usr/share/GeoIP/GeoLite2-Country.mmdb(用户组名依实际进程用户调整) - 验证是否生效:在日志中加
$geoip2_metadata_country_iso_code变量,tail -f /var/log/nginx/access.log看真实请求是否带出CN、US等值 - 如果始终为空,大概率是路径写错、文件损坏、或
source=$remote_addr被 CDN 代理覆盖——此时得用$http_x_forwarded_for并配合set_real_ip_from


















