2026年11月12日
2026年11月12日
Nginx 用 GeoIP2 按国家限制访问|geoip2 模块、GeoLite2 更新与 WP 后台限制
本文是日语原文的本地化版本。 阅读日语原文
要在 Nginx 中按国家限制访问,现在的做法是:使用 GeoIP2 模块(ngx_http_geoip2_module)和 MaxMind 的 GeoLite2 数据库,用 geoip2 块把国家代码存入变量,再用 map 决定放行还是拒绝(截至 2026 年 11 月)。旧文章里“用 wget 下载 GeoLite 的 .dat 文件”这种做法,因为数据已停止分发,现在已经行不通。
先说结论。
- 旧的
ngx_http_geoip_module(GeoIP Legacy)依赖的旧格式数据,MaxMind 已停止分发,新部署不能再用。请改用 GeoIP2(.mmdb格式)。 - GeoLite2 免费,但需要注册 MaxMind 账号并获取许可证密钥(License Key),下载和更新用
geoipupdate完成。 - 在 Nginx 一侧,用
geoip2块生成$geoip2_country_code之类的变量,用map得出相当于allow/deny的 0 / 1,再在location中return 403。 - 如果站点经过 Cloudflare 等 CDN,必须先用
real_ip_header还原访客的真实 IP,否则所有请求都会被判定为 CDN 所在的国家。 - 按国家限制只是“减少攻击的辅助手段”,用 VPN 或中转就能绕过。比起对整个公开页面使用,把它限定在登录页和后台上更现实。
旧版 geoip 与 GeoIP2 的区别
| 项目 | ngx_http_geoip_module(旧) | ngx_http_geoip2_module(GeoIP2) |
|---|---|---|
| 数据格式 | GeoIP Legacy(.dat) |
GeoIP2(.mmdb) |
| 免费数据 | 已结束(2019 年停止分发) | GeoLite2(需要注册账号并获取许可证密钥) |
| 所需库 | libGeoIP | libmaxminddb |
| 模块来源 | Nginx 自带(--with-http_geoip_module) |
第三方模块 leev/ngx_http_geoip2_module。Debian / Ubuntu 有现成的软件包 |
| 变量 | $geoip_country_code 等固定名称 |
在 geoip2 块中自己命名 |
如果现有配置还在使用 geoip_country,那么数据库已经停止更新,判定结果也已经过时。建议趁这个机会迁移到 GeoIP2。
步骤 1:注册 MaxMind 并创建许可证密钥
- 在 MaxMind 的 GeoLite2 页面创建免费账号。
- 登录后,在账号管理页面生成 License Key(密钥只在生成时显示一次,请先记下来)。
- 要使用的数据库:只需要国家的话用
GeoLite2-Country,需要精确到城市的话用GeoLite2-City。按国家限制的话,Country 就够了。
使用条款(GeoLite2 End User License Agreement)中有关于数据再分发和展示的规定。如果只是在公司自己的服务器上用于判定,通常没有问题,但请在注册时确认条款。
步骤 2:用 geoipupdate 获取并更新数据库
GeoLite2 会定期更新,所以不要手动下载,而是使用 geoipupdate(参见 geoipupdate 文档)。
# Debian / Ubuntu
sudo apt install geoipupdate
# RHEL 系(AlmaLinux / Rocky Linux)从 EPEL 安装
sudo dnf install geoipupdate
在配置文件 /etc/GeoIP.conf 中写入账号 ID 和许可证密钥。
AccountID 123456
LicenseKey 此处填写你生成的密钥
EditionIDs GeoLite2-Country
# 保存位置为发行版默认值(Debian / Ubuntu 是 /var/lib/GeoIP,其他多为 /usr/share/GeoIP)。
# 要更改时写 DatabaseDirectory /var/lib/GeoIP
sudo geoipupdate -v
ls -l /var/lib/GeoIP/ # 看到 GeoLite2-Country.mmdb 就说明成功
软件包版的 geoipupdate 已经配置了通过 systemd 定时器或 cron 定期运行(可用 systemctl list-timers | grep geoip 确认)。如果没有,就添加一个大约每周一次的 cron。
步骤 3:在 Nginx 中安装 GeoIP2 模块
Debian / Ubuntu(apt 安装的 Nginx)
sudo apt install libnginx-mod-http-geoip2
ls /etc/nginx/modules-enabled/ # 会放置 50-mod-http-geoip2.conf
nginx -t
nginx.org 的软件包或源码编译的 Nginx
先安装 libmaxminddb,再用与正在运行的 Nginx 相同版本的源码编译动态模块。
sudo apt install libmaxminddb-dev # RHEL 系是 libmaxminddb-devel
cd /usr/local/src
git clone https://github.com/leev/ngx_http_geoip2_module
curl -LO https://nginx.org/download/nginx-1.26.3.tar.gz # 版本与 nginx -v 保持一致
tar xzf nginx-1.26.3.tar.gz && cd nginx-1.26.3
./configure --with-compat --add-dynamic-module=../ngx_http_geoip2_module
make modules
sudo cp objs/ngx_http_geoip2_module.so /etc/nginx/modules/
在 nginx.conf 的开头加载(软件包版会自动放置)。
load_module modules/ngx_http_geoip2_module.so;
步骤 4:把国家代码存入变量并决定放行或拒绝
写在 http 块中。先用 geoip2 块把国家代码存入变量,再用 map 把允许的国家设为 1,其余设为 0。
http {
geoip2 /var/lib/GeoIP/GeoLite2-Country.mmdb {
auto_reload 60m;
$geoip2_country_code country iso_code;
}
# 允许访问后台的国家(ISO 3166-1 的两位字母代码)
map $geoip2_country_code $admin_allowed {
default 0;
JP 1;
"" 1; # 无法判定(私有 IP 等)的放行。想拒绝就改为 0
}
}
auto_reload 是在不重启的情况下,重新读取被 geoipupdate 替换的数据库的时间间隔。
步骤 5:只限制 WordPress 的登录页和后台
这个例子不限制公开页面,只按国家限制 wp-login.php 和 /wp-admin/。写在 server 块中。
server {
# ... 现有配置 ...
location = /wp-login.php {
if ($admin_allowed = 0) { return 403; }
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ^~ /wp-admin/ {
if ($admin_allowed = 0) { return 403; }
# 如果外部插件会用到 admin-ajax.php,请用单独的 location 放行
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
}
if 与 return 的组合是 Nginx 可以安全处理的写法。/wp-admin/admin-ajax.php 有时会被公开页面的功能(表单、无限滚动等)在未登录状态下调用,因此是否把它排除在限制之外,要根据站点的功能来决定。
生效与确认:
sudo nginx -t && sudo systemctl reload nginx
# 从自己所在的国家访问:返回 200 或 302
curl -sI https://example.com/wp-login.php | head -1
# 确认判定结果(在日志中输出国家代码的示例)
# log_format geo '$remote_addr $geoip2_country_code "$request" $status';
要确认从海外访问时的表现,可以使用海外节点的 VPN,或国外的监控服务访问 wp-login.php,看是否返回 403。
经过 CDN(Cloudflare 等)的情况
经过 CDN 时,$remote_addr 会变成 CDN 的 IP,所有访客都会被判定为 CDN 所在的国家。请先用 ngx_http_realip_module 还原访客的真实 IP(参见 realip 模块)。
# Cloudflare 的示例。IP 列表请定期从 Cloudflare 官方的 IP Ranges 更新
set_real_ip_from 173.245.48.0/20;
# ... 列出 Cloudflare 的全部网段 ...
real_ip_header CF-Connecting-IP;
geoip2 默认读取 $remote_addr,因此会用 realip 替换后的真实 IP 来判定。Cloudflare 本身也有按国家的访问控制(WAF 自定义规则),所以如果你在用 Cloudflare,在 Cloudflare 一侧限制比在 Nginx 中限制更简单。也请参考为 WordPress 设置 Cloudflare。
按国家限制的局限
- GeoIP 的判定是基于 IP 地址登记信息的推测,用 VPN、代理或海外云服务器中转就能绕过。
- 在移动网络或企业网络中,可能被判定为与实际所在地不同的国家。出差在海外的管理员被拒之门外,也是运维上会遇到的问题。
- 因此,按国家限制应作为减少登录暴力破解的辅助手段,与双重验证、登录次数限制、用 Basic 认证保护 wp-admin 组合使用。
常见问题
Nginx 的 geoip 模块和 geoip2 模块有什么区别
旧的 geoip 模块读取 GeoIP Legacy 格式(.dat)的数据,但 MaxMind 已停止分发这种格式。geoip2 模块(ngx_http_geoip2_module)读取现在的 .mmdb 格式,可以使用 GeoLite2 的免费数据。新的配置请使用 geoip2。
GeoLite2 免费吗?需要注册吗
免费,但需要注册 MaxMind 账号并生成许可证密钥。下载和定期更新用 geoipupdate 完成。不使用密钥、直接下载的旧做法已经行不通。
按国家限制就能防止 WordPress 的非法登录吗
它能减少暴力破解的尝试,但用 VPN 或海外服务器中转就能绕过,所以不能说可以防住。请与登录次数限制、双重验证、Basic 认证等组合使用。
使用 Cloudflare 时所有访客都变成同一个国家
这是因为经过 CDN 时,连接来源变成了 CDN 的 IP。请先用 set_real_ip_from 和 real_ip_header CF-Connecting-IP 还原真实 IP,再进行判定。也可以用 Cloudflare 的 WAF 按国家限制。
用 GeoLite2-City 能限制到城市吗
城市和地区的代码也可以存入变量,但城市级别的精度比国家级别低,容易误判,把正当用户拒之门外。作为访问控制,只做到国家级别比较稳妥。
总结
- 现在 Nginx 的按国家限制用 GeoIP2(ngx_http_geoip2_module + GeoLite2
.mmdb)完成,数据用geoipupdate更新。 - 用
geoip2把国家代码存入变量,用map决定放行或拒绝,在wp-login.php和/wp-admin/的location中return 403。 - 经过 CDN 时先还原真实 IP,并把按国家限制作为与双重验证等组合使用的辅助措施。
相关文章
- 如何限制对 WordPress REST API 的访问
- 用 htpasswd 创建 Apache 的 Basic 认证密码|保护 wp-admin
- 为 WordPress 设置 Cloudflare 的完整指南
参考:ngx_http_geoip2_module(GitHub)、MaxMind GeoLite2 Free Geolocation Data、MaxMind – Updating Databases(geoipupdate)、Nginx ngx_http_realip_module
更新记录:2026-11-09 全面改写(将已停止分发的旧 GeoLite 做法替换为 GeoIP2 与 geoipupdate 的做法,并新增 WordPress 后台限制示例和 CDN 注意事项)。本文由 WordPress Hakase(日本 Lightscend 株式会社)运营。
关于 WordPress 有疑问?本站运营方位于日本,仅以日语回复。 联系我们(日语)