如何在 robots.txt 里放行或拦截 AI 机器人
大多数网站只决定过一次,而且决定得很糟,此后再没回头看过。要么是怕被拿去训练,一慌之下全部拦掉,顺手连带来客户的流量一起掐死;要么干脆什么都不配,任由每个爬虫按自己方便的方式去猜。
合理的立场有三种。下面是每一种对应的文件,以及那些会让它们前功尽弃的错误。
先说决定一切的那个区别
AI 公司跑的代理有两类,两者不能互相替换。
爬虫按自己的节奏批量收页面,通常是为了训练或建索引:GPTBot、ClaudeBot、PerplexityBot、CCBot、Bytespider、Google-Extended、Applebot-Extended。
按需抓取器只取一个页面,因为刚刚有人就这个页面提了问:ChatGPT-User、Claude-User、Perplexity-User。这次请求没人在训练任何东西,是有个人在等一句关于你的答案。
拦住前一类,是关于你内容的生意决定。拦住后一类,更接近于把一个手动输入你网址的访客挡在门外。你会读到的那些「把 AI 全拦了」的建议,基本不区分这两者。
情况一 —— 放它们进来
凡是希望被找到的东西,这就是正确的默认值:文档、想要被署名引用的媒体、想被推荐的商家。
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /cart/
Disallow: /*?session=
Content-Signal: search=yes, ai-input=yes, ai-train=no
Sitemap: https://example.cn/sitemap.xml
看清楚它说了什么:进来吧,收录我,拿我去回答问题,但别拿我训练。这个立场光靠 robots.txt 表达不出来,所以才有后面的 Content-Signal 那一节。
情况二 —— 回答可以,训练不行
多数企业真正想要的就是这一种,而几乎没人把它配对。
User-agent: GPTBot
Disallow: /
User-agent: ClaudeBot
Disallow: /
User-agent: CCBot
Disallow: /
User-agent: Bytespider
Disallow: /
User-agent: Google-Extended
Disallow: /
User-agent: Applebot-Extended
Disallow: /
User-agent: OAI-SearchBot
Allow: /
User-agent: Claude-SearchBot
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: ChatGPT-User
Allow: /
User-agent: Claude-User
Allow: /
User-agent: Perplexity-User
Allow: /
User-agent: *
Allow: /
Content-Signal: search=yes, ai-input=yes, ai-train=no
Sitemap: https://example.cn/sitemap.xml
关于这份文件有两点值得知道。Google-Extended 管的是 Gemini 的训练,不是 Google 搜索,拦掉它不影响你的搜索排名。Applebot-Extended 是同样的道理,它对应 Apple Intelligence,和支撑 Siri 与 Spotlight 的 Applebot 是两回事。
情况三 —— 彻底拦住 AI
对付费墙后的档案、有授权的素材,以及任何你卖的是访问权的内容,这么做站得住脚。
User-agent: GPTBot
User-agent: OAI-SearchBot
User-agent: ChatGPT-User
User-agent: ClaudeBot
User-agent: Claude-SearchBot
User-agent: Claude-User
User-agent: PerplexityBot
User-agent: Perplexity-User
User-agent: CCBot
User-agent: Bytespider
User-agent: Google-Extended
User-agent: Applebot-Extended
User-agent: Amazonbot
User-agent: meta-externalagent
Disallow: /
User-agent: *
Allow: /
Content-Signal: search=no, ai-input=no, ai-train=no
Sitemap: https://example.cn/sitemap.xml
连着写的 User-agent 行共享它们下面的规则。这是合法写法,也比十四个各写各的段落好维护得多。
选之前先想清楚代价:你会从 AI 的回答里消失。有人向助手打听你这个行业时,被点名的是你的竞争对手,不是你。
那些错误
只有最具体的那一组会生效。 爬虫一旦匹配到自己的名字,就会完全无视 User-agent: *。所以下面这段并不像看上去那样管用:
User-agent: *
Disallow: /private/
User-agent: GPTBot
Disallow: /
GPTBot 确实被全站拦下了,但其他机器人只读 * 那一组;而且哪天你在 GPTBot 下面加一条 Allow,/private/ 对它就不再有任何保护,因为那一组从头到尾没提过 /private/。
空行是分组的分隔符。 一组中间的空行会把这组就此结束,后面的行在下一个 User-agent 出现之前不属于任何人。
Disallow: 后面什么都不写,意思是全部允许。 Disallow: / 才是全部拦住。开着的门和关上的门,差一个字符。
robots.txt 不是访问控制。 它是一个请求,守规矩的爬虫会照办。不守规矩的抓取器过去无视它,以后也照样无视。真正不能被读到的东西需要的是身份验证,不是一条指令。
Crawl-delay 基本被 AI 爬虫忽略。 如果问题出在负载,就在边缘做限流。
Content-Signal:说清用途,而不只是能不能
robots.txt 只回答一个问题:这个你能不能抓。它没有办法表达「可以收录我,但别拿我训练」,而这恰恰是大多数网站真正的立场。
Content-Signal 用三个互相独立的信号补上这一层:
Content-Signal: search=yes, ai-input=yes, ai-train=no
search—— 这些内容能否出现在搜索索引里ai-input—— 能否作为生成答案的材料,并注明出处ai-train—— 能否用来训练模型
它像上面那样写进 robots.txt,也可以作为 HTTP 响应头随每个响应一起发出,我们就是这么做的。这是个年轻的约定,不是谁有义务遵守的标准,但它只占一行、机器能读,而且表达了 Disallow 表达不了的意图。
确认你的那份真的送到了
写好文件和把它发出去,是两码事:
curl -s https://example.cn/robots.txt
curl -sI https://example.cn/robots.txt | grep -i "server\|cf-cache-status"
第二条命令比看上去重要。如果你在 CDN 后面,而自己没有一份 robots.txt,CDN 可能会替你回答一份你从没写过的文件:一个看着很像样的 200,里面没有你的任何一条规则,也没有你的 sitemap。我们的就这样持续了好几个月,而源站那边一直返回 404。