Nginx 实现基于地理位置的动态访问频率限制需启用 geoip2 模块并配合 MaxMind 数据库,通过 map 指令将国家码映射为有限 key,再用 limit_req_zone 统一限流区域实现差异化速率控制。

在 Nginx 中实现基于地理位置的动态访问频率限制,核心是结合 geoip2(或旧版 geoip)模块获取客户端国家/地区信息,并通过 limit_req 配合不同 key 的限流区域(zone)实现差异化速率控制。这需要编译时启用地理库支持,且不能仅靠基础限流指令完成。
准备地理信息数据与模块支持
Nginx 默认不内置 IP 地理定位能力,需确保已启用 ngx_http_geoip2_module(推荐)或 ngx_http_geoip_module(已弃用)。操作要点:
- 使用 ngx_http_geoip2_module,配合 MaxMind GeoLite2 City 或 Country 数据库(
.mmdb文件) - 编译 Nginx 时添加
--add-dynamic-module=../ngx_http_geoip2_module,或使用已集成该模块的发行版(如 OpenResty) - 将
GeoLite2-Country.mmdb放入指定路径(如/usr/share/GeoIP/GeoLite2-Country.mmdb)并确认 Nginx 进程有读取权限
定义按国家分组的限流区域
不能直接对 $geoip2_data_country_code 做限流(因变量在 limit_req_zone 中不可用),需预先映射为有限的、可命名的 key。常用做法是用 map 指令做分类聚合:
示例:对高风险国家(如 CN、RU、BR)设宽松限流,对常规国家(US、DE、JP)设中等,对可信小国(CH、NZ)设严格
http {
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
$geoip2_country_code source=$remote_addr country iso_code;
}
<pre class="brush:php;toolbar:false;">map $geoip2_country_code $rate_limit_key {
default "default";
~^(CN|RU|BR)$ "high_volume";
~^(US|DE|JP|GB)$ "medium_volume";
~^(CH|NZ|SE|CA)$ "low_volume";
}
limit_req_zone $rate_limit_key zone=per_country:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=5r/s;}
说明:map 将原始国家码映射为三类逻辑 key,再统一绑定到一个共享 zone;这样避免为每个国家建 zone 导致内存爆炸。
在 server 或 location 中应用差异化限流
使用 limit_req 引用上述 zone,并可叠加 IP 级兜底限流增强防护:
server {
listen 80;
server_name example.com;
<pre class="brush:php;toolbar:false;">location /api/ {
# 先按国家策略限流
limit_req zone=per_country burst=20 nodelay;
# 再叠加单 IP 限流(防同一国家内恶意 IP 刷量)
limit_req zone=per_ip burst=5;
proxy_pass http://backend;
}}
注意:burst 和 nodelay 参数影响突发流量处理方式;若需更精细控制(如中国用户允许 30r/s,美国 15r/s),可为每类 key 单独定义 zone:
limit_req_zone $rate_limit_key zone=cn_group:10m rate=30r/s; limit_req_zone $rate_limit_key zone=us_group:10m rate=15r/s; # ……再用 if 或 map + 多个 limit_req(需配合第三方模块如 lua-nginx-module 实现条件加载)
纯原生 Nginx 不支持“根据变量值选择不同 zone”,因此推荐第一种聚合 key + 统一 zone 的方案,平衡灵活性与稳定性。
验证与调试技巧
上线前务必验证地理识别与限流是否生效:
- 用
curl -H "X-Forwarded-For: 1.2.3.4" http://example.com/api/test测试不同 IP 对应的国家码(可在日志中记录$geoip2_country_code) - 在
log_format中加入$rate_limit_key和$limit变量,观察实际触发的限流类型 - 用
ab或hey工具模拟并发请求,检查响应头是否含X-RateLimit-Limit(需自定义返回头)或 HTTP 429 状态码
不复杂但容易忽略:真实环境常经 CDN 或反向代理,务必确保 $remote_addr 是真实客户端 IP(通过 real_ip_header X-Forwarded-For 和 set_real_ip_from 正确配置)。











