Nginx中需用map指令基于User-Agent实现差异化限流:先通过map提取ua_type变量,再结合limit_req_zone按类型或IP+UA组合设置独立限流策略,并用if快速拦截恶意UA。

在Nginx中,单纯用 limit_req 对所有请求统一限流无法区分“友好爬虫”和“恶意扫描器”。真正有效的频率控制,需要结合 User-Agent 特征做差异化策略——而 map 指令正是实现该目标的核心机制。
用 map 提取 User-Agent 类型标签
map 不是直接限流的指令,而是把原始请求头映射为一个可复用的变量。关键在于:它支持正则匹配、大小写不敏感、可嵌套默认值,且在 server 块外定义,性能开销极低。
在 http 块中添加:
map $http_user_agent $ua_type {
default "normal";
~*bingbot|bingpreview "search_bot";
~*googlebot|googleweblight "search_bot";
~*yandexbot|duckduckbot "search_bot";
~*semrush|ahrefsbot|mj12bot "seo_tool";
~*nmap|sqlmap|nikto|dirbuster "scanner";
~*python-requests|curl|httpie "script_client";
}说明:
• $http_user_agent 是 Nginx 内置变量,自动提取请求头中的 User-Agent 字段
• ~* 表示忽略大小写的正则匹配
• 匹配顺序从上到下,第一个命中即终止,所以高危词(如 sqlmap)建议前置
• 未匹配到的全部归为 "normal",便于后续统一处理
为不同 UA 类型配置独立限流区域
限流需配合 limit_req_zone 使用,而 zone 的 key 可以是任意字符串组合。这里利用 $ua_type 实现“按类型隔离”:
limit_req_zone $ua_type zone=ua_limit:10m rate=10r/s; limit_req_zone $binary_remote_addr$ua_type zone=ip_ua_limit:10m rate=5r/s;
两个 zone 的区别:
• ua_limit:限制某类 UA(如所有 scanner)每秒最多 10 次请求,不区分 IP
• ip_ua_limit:限制“同一 IP + 同一类 UA”的组合,例如单个 IP 发起的 seo_tool 请求每秒最多 5 次
• 10m 表示共享内存区大小,一般 1m 可存储约 1.6 万个 key,按需调整
在 location 中应用差异化限流策略
有了变量和 zone,就可以在具体路径中灵活调用。例如对 API 接口严格限制扫描器,但允许搜索引擎缓存:
location /api/ {
# 扫描器直接拒绝,不进入限流队列
if ($ua_type = "scanner") {
return 403;
}
<pre class="brush:php;toolbar:false;"># 搜索引擎放宽限制(每秒 30 次),其他 UA 严格限制(每秒 5 次)
limit_req zone=ua_limit burst=20 nodelay;
limit_req_status 429;
# 同时叠加 IP+UA 维度限制,防止单 IP 滥用某类 UA
limit_req zone=ip_ua_limit burst=10;}
注意:
• if 在 location 中可用(非 server 级),用于快速拦截高危 UA,比进入限流逻辑更高效
• burst 允许突发流量缓冲,nodelay 表示不延迟响应(适合宽松策略)
• limit_req_status 统一返回 429,方便前端识别限流状态
验证与调试技巧
上线前务必确认 map 变量是否生效:
- 临时在 location 中加入
add_header X-UA-Type $ua_type;,用 curl 查看响应头 - 用
nginx -t检查语法,map 定义错误会导致 reload 失败 - 观察 error.log:被限流请求会记录
limiting requests,被 if 拦截的则记为client denied by rule - 测试时用不同 UA 字符串,例如:
curl -H "User-Agent: sqlmap/1.7" http://your.site/api/test
实际部署后,可通过 limit_req_log_level warn 将限流事件提升为 warn 级别,便于日志聚合分析。











