robots.txt + nginx 双保险劝退采集爬虫(附必须放行 robots.txt 的原因)-第三维度MokIII

谁在爬你的站

个人站最常遇到的不是"恶意攻击",而是一堆 SEO 数据商 / 采集爬虫在持续抓取:

AhrefsBot、SemrushBot、DataForSeoBot、MJ12bot、DotBot、BLEXBot、ZoominfoBot、Amazonbot、serpstatbot、SeznamBot、Cliqzbot、MauiBot、Barkrowler、Megalodon……

它们对你的站没有 SEO 价值(不会带来流量,也不会提升排名),但会:

  • 吃掉带宽(尤其是 5M 小带宽的机器);
  • 占用 PHP-FPM worker(回源一次就是一次开销);
  • 在访问日志里刷出大量噪音,把真正的问题淹没。

一、两种手段的本质区别

手段性质成本有效性
robots.txt 里 Disallow声明式极低——爬虫读到之后根本不会发请求只对"守规矩"的爬虫有效
nginx 返回 403强制式每条 URL 仍要回源一次(你花 CPU 去拒绝)对伪造 UA 的也有效

所以正确做法是两个一起用:

  • robots.txt 负责劝退大部分(成本低、省掉绝大部分请求);
  • nginx 403 负责兜底不守规矩、伪造 UA 的那些。

二、⚠️ 关键坑:nginx 拦截必须放行 /robots.txt

这是"双保险"里最容易出的错。

如果你在 nginx 里按 UA 一律 return 403,那么爬虫连 /robots.txt 都读不到——它不知道你要求它别抓,只能继续一条条试探。

于是你既没省下请求,又白白浪费了 robots.txt 的作用。这套配置必须成对出现。

对应写法:先按 UA 判定,再给 robots.txt 开一个例外。

set $deny_by_ua 0;

if ($http_user_agent ~* "(DataForSeoBot|SemrushBot|AhrefsBot|MJ12bot|DotBot|BLEXBot|ZoominfoBot|Amazonbot|Megalodon|serpstatbot|SeznamBot|Cliqzbot|MauiBot|Barkrowler|serpstat|SERankingBacklinksBot|SERankingBot)") {
    set $deny_by_ua 1;
}

if ($uri = /robots.txt) { set $deny_by_ua 0; }   # ← 这一行不能少

if ($deny_by_ua = 1) { return 403; }

用「变量 + 单个 if 判最后」的写法,而不是在一个 if 里堆一串条件——可读性好,也不容易踩 nginx if 的嵌套坑。

三、robots.txt 里对应的声明

实体文件(放站点根目录,nginx 会优先命中它、不再进 PHP):

User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php

Sitemap: https://www.example.com/sitemap_index.xml

User-agent: AhrefsBot
Disallow: /

User-agent: SemrushBot
Disallow: /

# ... 其余爬虫各一段

两个细节:

  • 每一段都是独立的 User-agent + Disallow: /,而不是写在一个 User-agent 下面用逗号隔开——robots.txt 的语法不支持逗号列表;
  • Sitemap: 那一行要留,别忘了给搜索引擎留指路。

四、名单要保持两边一致

robots.txt 的 UA 分组和 nginx 的正则名单必须对应。不一致的后果是:

  • 只在 nginx 拦、不在 robots.txt 声明 → 爬虫一直试探,每条都回源;
  • 只在 robots.txt 声明、nginx 不拦 → 伪造 UA 的照样抓。

改名单时两个文件一起改,并在注释里写明"与另一个文件的名单保持一致"。

五、别误伤有用的爬虫

名单里绝对不要加:Googlebot、Baiduspider、bingbot、Bytespider(字节)等。

一个实用的自检习惯:改完名单,用真实 UA 测一遍:

# 期望 403
curl -sk -o /dev/null -w '%{http_code}\n' -A 'AhrefsBot/7.0' https://www.example.com/
# 期望 403
curl -sk -o /dev/null -w '%{http_code}\n' -A 'SemrushBot' https://www.example.com/
# 期望 200 ← 这条错了就是误伤
curl -sk -o /dev/null -w '%{http_code}\n' -A 'Mozilla/5.0 (compatible; Googlebot/2.1)' https://www.example.com/
# 期望 200 ← robots.txt 必须能读到
curl -sk -o /dev/null -w '%{http_code}\n' -A 'AhrefsBot/7.0' https://www.example.com/robots.txt

最后一条是最关键的验收点。

六、回滚

删掉 nginx 那段(或整个独立配置文件)→ nginx -t && nginx -s reload。robots.txt 是实体文件,删掉即恢复 WordPress 动态输出。


一句话总结:对付采集爬虫要"声明 + 强制"两条腿——robots.txt 让守规矩的别来,nginx 403 拦住不守规矩的;但nginx 拦 UA 时必须放行 /robots.txt,否则爬虫读不到你的声明,只会继续试探。