网站反爬虫防护实战:从日志识别恶意采集到 Nginx 分层拦截,个人站零成本方案

先想清楚:你要防的是谁

做个人站这些年,我被爬虫坑过不止一次。最早以为「被爬是好事,说明内容有价值」,直到某天发现服务器 CPU 跑满、带宽被一个不知道哪来的爬虫吃到只剩一丝,正常访客打开页面要十几秒,才意识到爬虫分几种,得分开对待。

把爬虫按目的粗分四类,处理策略完全不同:

  • 搜索引擎蜘蛛(百度/Bing/Google):这是你求之不得的流量来源,绝不能一刀切封杀。要做的是用 robots.txt 和 sitemap 引导它抓正确的内容,而不是用防火墙误伤它。
  • RSS 阅读器、社交平台预览:通常 UA 明确、频率温和,放行即可。
  • 内容采集器(恶意扒站):整站抓取你的原创内容搬运到别的站,频率高、并发大、不带正常 Referer,是你真正要防的。
  • 漏洞扫描器 / 攻击探测:抓页面是假,找后台、找注入点是真,通常伴随 404/500 大爆发。

反爬的第一原则是:先量后治,别凭感觉封。很多人一上来就往 Nginx 里加一堆 UA 黑名单,结果先把自己的监控探针和 RSS 阅读器封了。正确的顺序是——先通过日志看清谁在爬、爬什么、多快,再做针对性拦截。

第一步:用访问日志识别异常流量

Nginx 的 access_log 是最直接的证据。建议在日志格式里加上 $remote_addr、$http_user_agent、$http_referer、$request_time、$body_bytes_sent,然后做聚合分析。

统计单位时间内的请求数,按 IP 排序,找出「请求量远超正常访客」的源头:

# 统计当天请求数最高的前 20 个 IP
awk '{print $1}' /var/log/nginx/access.log \
  | sort | uniq -c | sort -rn | head -20

看请求的分布:如果某个 IP 短时间内访问了大量详情页 URL、几乎不加载静态资源(CSS/JS/图片)、没有正常的人类浏览节奏,那基本就是采集器。正常访客打开一个页面会连带请求十几个静态资源,而采集器往往只抓 HTML。

再按 UA 聚合,看看有没有 UA 字段可疑的:

awk -F'"' '{print $6}' /var/log/nginx/access.log \
  | sort | uniq -c | sort -rn | head -20

一个常见现象:某个 IP 的 UA 是空的、或者是伪造的常见浏览器 UA,但请求频率高得离谱。这就是典型的脚本采集——它懒得伪装完整,只填了个能过静态检查的 UA。

第二步:分层拦截策略

反爬不该只有一个开关,而应该分四层,从轻到重,能挡住的绝不升级手段:

第一层:robots.txt 与速率协商

对「守规矩的爬虫」,robots.txt 是成本最低的约束。明确 Disallow 后台、搜索页、带参数的动态页(这些页面被抓取既浪费你资源又可能产生无限 URL),并声明 Crawl-delay(虽然很多搜索引擎已不支持,但一部分小爬虫认)。对于 AI 爬虫(GPTBot、CCBot 等),你可以在 robots.txt 里单独决定是否允许——不想被拿去训练就明确 Disallow。

第二层:Nginx 限速(limit_req / limit_conn)

这是最有效、最不容易误伤的一层。用 limit_req 限制单 IP 的请求速率,用 limit_conn 限制并发连接数。关键在于「削峰不拒绝」:用 burst 允许短时突发,超过的部分排队或直接拒绝,正常访客的体验不受影响,而持续性爬取会被卡住。

# 在 http 块定义限流区
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

# 在 location 里应用
location / {
    limit_req zone=perip burst=20 nodelay;
    limit_conn addr 20;
}

这里 rate=10r/s 意味着单个 IP 每秒最多 10 个请求,burst=20 允许突发 20 个。注意如果你前面有 CDN,这里的 $binary_remote_addr 拿到的是 CDN 节点的 IP,必须先用 real_ip 模块还原真实 IP,否则所有访客会被算成同一个 IP 一起被限,那就是灾难。还原配置见下文。

第三层:按 UA / Referer 拦截

对已知的恶意采集器 UA,可以用 Nginx 直接返回 403。但要注意这层容易被绕过(UA 可以随便改),所以只作为补充:

if ($http_user_agent ~* (scrapy|python-requests|go-http-client|curl|wget)) {
    return 403;
}

谨慎使用 if 指令:Nginx 的 if 在 location 里行为诡异,尽量把这种判断放在 server 层级,或用 map 更稳妥。map 的写法是定义一个变量,在请求处理早期就完成判断,没有 if 的作用域陷阱。

第四层:JS 挑战与行为验证

当采集器开始伪造 UA、模仿正常请求时,前面的静态规则就失效了。这时候需要引入「只有真浏览器才能完成」的挑战——比如让客户端执行一段 JS 计算并带上结果,或用 Cloudflare Turnstile 这类无感验证。个人站如果已经挂了 Cloudflare,直接开它的 Bot Fight Mode 或 WAF 自定义规则是最省事的,不用自己写逻辑。

但要注意:JS 挑战对 SEO 有风险(搜索引擎能否执行 JS 因引擎而异),绝不能对搜索引擎蜘蛛开启挑战。做法是对已知搜索引擎 IP 段或已验证的搜索引擎 UA 放行,只对可疑流量挑战。

关键前提:真实 IP 还原

无论用哪层拦截,只要站前有 CDN 或反向代理,你就必须先解决「拿到的是谁的 IP」这个问题。否则限流、黑名单、封禁全部作用在代理节点 IP 上,效果是零,副作用是全站被限。

Nginx 需要 ngx_http_realip_module(官方编译默认带),配置如下:

# 告诉 Nginx 信任哪些来源代理的 X-Forwarded-For
set_real_ip_from 173.245.48.0/20;   # 换成你的 CDN 回源 IP 段
set_real_ip_from 103.21.244.0/22;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

real_ip_recursive on 表示逐层剥离可信代理、取第一个不可信来源为真实 IP。配置完后,$remote_addr 就是访客真实 IP 了,限流才有意义。验证方法:把 $remote_addr 打进自定义日志,自己访问一次看记录的是不是你家的 IP。

别把自己人也封了:白名单与误伤防护

我在一次「一刀切封 IP」之后就学乖了:那次我用脚本把当天请求量前 50 的 IP 全封了,结果把自己服务器的监控探针(每分钟拉一次站点可用性)和一位长期读者的 RSS 阅读器(它每小时抓一次全文)也封了,监控直接报警「站点不可用」,虚惊一场。

教训是:拦截规则上线前,一定要有白名单机制。至少包括:

  • 你自己的服务器/办公 IP(做监控和后台管理)。
  • 已知的搜索引擎蜘蛛 IP 段(百度、Bing、Google 都会公布)。
  • 你依赖的第三方服务回调 IP(如支付回调、Webhook)。

在 Nginx 里用 geo 或 map 把这些 IP 标记为可信,直接跳过所有限流和拦截:

geo $trusted {
    default 0;
    203.0.113.7 1;      # 你的监控探针
    198.51.100.0/24 1;  # 某个可信网段
}
# 然后 limit_req 用 map 把可信 IP 的限流 key 设空,即不参与限流

一个务实的组合方案

把上面的东西拼起来,个人站最低成本的反爬组合是:

  • 站前挂 Cloudflare 免费版:享受它的基础 DDoS 防护和 Bot 规则,同时隐藏源站 IP(源站只允许 Cloudflare 回源 IP 访问)。
  • Nginx 做真实 IP 还原,让后面的限流作用在真实访客 IP 上。
  • limit_req + limit_conn 兜底,挡住持续性高并发抓取。
  • robots.txt 引导搜索引擎、拒绝 AI 训练爬虫(按你的意愿)。
  • 日志定期分析(GoAccess 或自写脚本),每周看一眼有没有新出现的异常源。

这套组合不需要写复杂代码,成本几乎为零,能挡掉 90% 的低成本采集器。真正高水平的对手(分布式 IP 池、无头浏览器)个人站是防不住的,也不值得为它上重型方案——那种情况下,内容侧的策略(比如对高频访问的可疑 IP 返回缓慢降级的页面、给正文加水印)反而更实际。

总结

反爬的本质是「区分善意爬虫、恶意采集与正常访客三方」,而不是「一封了之」。落地路径:先用日志量化异常流量,再按 robots.txt → 限流 → UA/Referer → JS 挑战四层递进处理,务必先解决代理下的真实 IP 还原问题,并且任何拦截规则上线前都要有白名单兜底。对个人站长,最划算的做法是「Cloudflare 免费防护 + Nginx 限流兜底 + 日志定期巡检」这三件套,投入产出比远高于自研复杂的反爬系统。记住最重要的一条:你防的是持续高并发的抓取行为,不是某个特定的 UA 或某个 IP——理解这一点,你的策略就不会轻易被绕过,也不会误伤自己的流量。

Last modification:October 14th, 2026 at 09:30 pm

Leave a Comment