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 或中转就能绕过。比起对整个公开页面使用,把它限定在登录页和后台上更现实。
Nginx GeoIP2 配置流程:注册 MaxMind,用 geoipupdate 获取数据库,加载模块,用 geoip2 和 map 判定,再用 location 限制

旧版 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 并创建许可证密钥

  1. 在 MaxMind 的 GeoLite2 页面创建免费账号。
  2. 登录后,在账号管理页面生成 License Key(密钥只在生成时显示一次,请先记下来)。
  3. 要使用的数据库:只需要国家的话用 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,并把按国家限制作为与双重验证等组合使用的辅助措施。

相关文章

参考: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 有疑问?本站运营方位于日本,仅以日语回复。 联系我们(日语)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲