必须用map指令在http块顶层定义$blocked_ua变量,再在location中用if($blocked_ua) return 403——这是唯一兼顾安全、可维护与性能的方案;禁止在location内直接写正则匹配或混用proxy_pass。

在 Nginx 的 location 块中直接拦截恶意 User-Agent 是可行的,但必须严格遵循安全写法——只用 if + return,且禁止在其中调用 proxy_pass、rewrite 或其他可能引发重写冲突的指令。核心是轻量判断、立即响应、不干扰主流程。
推荐写法:在 location 内用 if 判断 $blocked_ua 变量
不要在 location 里重复写正则匹配,而是复用 http 块顶层定义的 map 变量(如 $blocked_ua)。这样既避免重复编译、又保持逻辑集中:
- 先在
http { }块最上方定义map $http_user_agent $blocked_ua,覆盖 sqlmap、nuclei、ZmEu、空 UA、Go-http-client/1.1 等典型特征 - 在目标
location /或location ~ \.(php|html)$中写:if ($blocked_ua) { return 403; } - 确保该
if是location内唯一控制逻辑,不嵌套、不跟proxy_pass同级或混用
哪些 User-Agent 字符串值得拦截
重点关注具备明确攻击意图或行为异常的 UA,而非所有爬虫:
- 已知扫描工具名:sqlmap、nuclei、gau、dalfox、whatweb、acunetix、masscan、nmap、nikto
- 暴力探测类:ZmEu、CensysInspect, libwww-perl(旧 Perl 爬虫,常用于试探)
- 空或无效 UA:
User-Agent:(冒号后无内容)、User-Agent: -、User-Agent: () { :; }; echo vulnerable - 高频轻量客户端:
curl -I、wget -r、python-requests/2.*(若未授权访问 API)
必须避开的常见错误
这些写法会导致拦截失效、返回 502/404 或影响正常请求:
- 在
location外(如server块顶层)写if ($http_user_agent ~* "sqlmap") { return 403; }—— Nginx 会重置变量,可能跳过后续proxy_pass - 在同一个
location中写多个if判断(如 UA + 请求方法 + 路径),叠加嵌套易出错 - 正则漏写
~*(忽略大小写),导致SQLMAP或SqlMap漏放 - 没处理空格或换行:应补充
~* ^[[:space:]]*$匹配纯空白 UA
增强可观测性的小技巧
拦截不能只“堵”,还要“看得清”:
- 扩展日志格式,在
log_format中加入$blocked_ua和$http_user_agent,方便快速 grep 分析 - 对触发拦截的请求,加响应头:
add_header X-Blocked-Reason "UA-Blocked";,便于前端或 WAF 联动识别 - 搭配
limit_req对高频拦截 IP 限流,防反复试探:limit_req zone=ua_block burst=3 nodelay; - 初期建议先记录不拦截(
set $ua_action "log_only";),观察 24 小时再切到return 403


















