最有效轻量的恶意爬虫拦截方式是动态日志驱动的IP封禁。需结合deny指令、geo+if逻辑判断及定时脚本分析访问日志,自动更新黑名单并设TTL防误封。

直接封禁已知恶意爬虫的IP是最有效、最轻量的拦截方式——但单纯靠deny指令在高变环境里容易失效,必须配合日志分析和动态更新机制。
用 deny 指令静态封禁 IP 是最基础的做法
它写在 location 或 server 块里,按顺序匹配,遇到第一条就终止判断。常见错误是把 deny all 写在前面,导致所有请求都被拦住。
-
deny必须放在所有allow之后,且以deny all收尾(如果启用了白名单逻辑) - 支持 CIDR 表示法,比如
deny 203.0.113.45;或deny 2001:db8::/32; - 不能嵌套在
if里使用,Nginx 会报错:"deny" directive is not allowed here - 修改后必须执行
nginx -t && nginx -s reload,否则不生效
把黑名单抽成独立文件更利于维护
当要封的 IP 超过 5 个,或者多个 server 都要共用同一套规则时,硬编码在配置里会很快失控。推荐用 include 引入外部文件。
- 创建文件如
/etc/nginx/conf.d/blacklist.conf,内容只需写规则,不加任何其他语句 - 每行一条
deny,最后一行仍是deny all;(除非你明确只封部分 IP) - 在目标
location中写include /etc/nginx/conf.d/blacklist.conf; - 注意文件权限:Nginx worker 进程必须有读取权限,否则
nginx -t会报open() failed (13: Permission denied)
用 geo + if 实现带逻辑的动态判断
当你需要“对某些 IP 封禁,但对其中个别例外放行”,或想把封禁逻辑复用于多个 location 时,geo 模块比一堆 deny 更清晰。
- 在
http块顶部定义:geo $is_blocked_ip { default 0; 192.168.1.100 1; 203.0.113.22/32 1; } - 在
location中写:if ($is_blocked_ip) { return 403; } - 这个方式不会干扰
allow/deny的原有流程,适合和白名单混合使用 - 注意:
if在location外不可用,且不能用于重写以外的复杂逻辑,否则易出意料行为
真正防住爬虫得靠日志驱动的动态封禁
静态黑名单只能应付已知 IP,而真实爬虫常换代理、用 IP 池。生产环境必须结合访问日志自动识别高频请求,再写入 Redis 或文件触发封禁。
- 先用
log_format记录关键字段:$remote_addr $http_user_agent $request_time $status - 用脚本(如 Python + awk)定时扫描日志,筛选出 1 分钟内 >50 次 GET /api/ 的
$remote_addr - 把命中 IP 写入
blacklist.conf并重载 Nginx,或推送到 Redis 由 Lua 脚本实时查 - 别忘了加清理逻辑:封禁 IP 超过 24 小时后自动移出,避免长期误伤
静态规则写错顶多 403,但动态封禁若没加 TTL 或没做去重,可能把整个 CDN 回源 IP 段误封——这点最容易被忽略。


















