GeoIP 地域限制利用 IP 归属地数据在接入层做访问控制:对于仅面向国内用户的业务,境外流量中绝大部分是扫描器与自动化攻击工具,通过 Nginx GeoIP 模块按国家拦截,可在请求到达应用之前挡掉大量恶意流量。本文给出基于 MaxMind GeoLite2 数据库的完整配置方案。
适用场景
- 企业官网、管理后台等业务只服务国内用户,无需开放海外访问
- 境外扫描与爆破流量占比高,浪费带宽并产生大量无效告警
- 希望在不采购高防产品的前提下降低暴露面
前置条件
- Nginx 已加载 GeoIP2 模块(检查方式见下)
- GeoLite2-Country.mmdb IP 归属地数据库
- Debian/Ubuntu 系统可直接 apt 安装模块;其他发行版或宝塔环境需重新编译 Nginx 加载
ngx_http_geoip2_module
检查模块是否已编译或加载:
nginx -V 2>&1 | tr ' ' '\n' | grep -i geoip
原理说明
GeoIP 模块在 Nginx 解析请求时,将客户端 IP 与本地 IP 归属地数据库(mmdb 格式)比对,得到国家代码,再通过 map 指令生成放行/拦截变量,由 if 或 location 决定返回 403 还是正常转发。整个过程在 Nginx 内完成,无外部请求,性能开销极低。
两个关键点:一是数据库需要定期更新,IP 段归属随时间变化,过期数据会产生误判;二是CDN 场景下必须先还原真实客户端 IP,否则判断的是 CDN 节点地址,境内 CDN 会导致规则完全失效。
操作步骤
1. 安装 GeoIP2 模块与数据库
# Debian/Ubuntu(使用发行版 nginx 时)
apt install -y libnginx-mod-http-geoip2
# 下载免费 IP 归属地数据库
mkdir -p /etc/nginx/geoip
cd /etc/nginx/geoip
wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb
2. 配置国家判定与放行规则
在 /etc/nginx/nginx.conf 的 http 块中加入:
geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
$geoip2_country_code country/iso_code;
}
map $geoip2_country_code $allowed_country {
default 0; # 默认拦截
CN 1; # 仅放行中国大陆 IP
}
如需放行香港地区用户,补充一行 HK 1; 即可。
3. 在站点中应用拦截
整站拦截(在 server 块中加入):
if ($allowed_country = 0) {
return 403;
}
更稳妥的做法是只对后台等敏感路径限制,不影响面向海外搜索引擎的公开页面:
location /admin {
if ($allowed_country = 0) {
return 403;
}
proxy_pass http://backend;
}
4. CDN/反代场景还原真实 IP
若站点前面套了 CDN,需在 http 块配置 real_ip,将 CDN 回源的 X-Forwarded-For 还原为真实客户端 IP(CDN 网段以服务商公布为准):
set_real_ip_from 0.0.0.0/0; # 生产环境替换为 CDN 官方网段列表
real_ip_header X-Forwarded-For;
real_ip_recursive on;
5. 配置数据库自动更新
创建 /etc/cron.weekly/update-geoip,每周更新数据库并重载 Nginx:
#!/bin/bash
cd /etc/nginx/geoip
wget -q https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb -O GeoLite2-Country.mmdb.new \
&& mv GeoLite2-Country.mmdb.new GeoLite2-Country.mmdb \
&& nginx -s reload
赋权:chmod +x /etc/cron.weekly/update-geoip
配置验证
先在测试 location 临时输出国家代码,确认判定正确:
location /geo_test {
return 200 "$geoip2_country_code\n";
}
重载后访问 https://www.example.com/geo_test,国内 IP 应返回 CN。验证通过后删除该 location。
再验证拦截:使用海外 VPS(或让海外朋友)访问受限路径,预期返回 403 Forbidden;国内访问正常返回页面。查看访问日志确认国家代码已记录:
log_format main '$remote_addr - $geoip2_country_code $request $status';
tail -f /var/log/nginx/access.log
常见问题
FAQ 1:套了 CDN 之后拦截不生效?
这是最常见的问题:Nginx 看到的客户端 IP 是 CDN 节点地址,境内 CDN 全部被判为 CN 而放行。必须按步骤 4 配置 set_real_ip_from,且网段列表要与 CDN 服务商公布的回源网段严格一致,否则攻击者可伪造 X-Forwarded-For 头绕过地域限制。
FAQ 2:误拦了海外正常用户或搜索引擎蜘蛛怎么办?
Google、Bing 的爬虫大多来自境外 IP,整站拦截会影响收录。建议二选一:① 只对后台、API 等敏感路径启用地域限制,公开页面放行;② 在拦截规则前对主流搜索引擎蜘蛛 UA 放行(注意 UA 可伪造,仅作为 SEO 兜底,不要对登录入口放行)。另外 GeoLite2 免费库对部分 IP 段归属判定不精确,误报时可先核对数据库更新时间。
总结
GeoIP 地域限制是低成本收敛暴露面的有效手段,配置要点有三:真实 IP 还原决定规则是否真正生效、数据库自动更新决定误判率、按路径粒度限制决定对业务与 SEO 的影响。它属于粗粒度防护,建议与 UA 黑名单、限频配置组合使用,构建多层次的接入防护。