robots.txt 大概是每个站长都写过、却最容易被当成草稿的文件:建站第一天随手抄一份,之后再也没打开过。可真出问题的时候,它往往是最难查的一环——页面在浏览器里打得开、返回码是 200、内容也正常,但搜索引擎就是不收录,或者反过来,本该保密的页面被完整抓走了。
这篇文章讲的不是「robots.txt 怎么写」这种抄模板的事,而是把它当成一份有作用域、有优先级、有生命周期的配置文件来管理。下面五个部分,每个都对应我在实际站点上踩过的具体问题。
一、先搞清楚它管什么、不管什么
robots.txt 是「抓取许可」声明,不是「访问控制」声明。它只影响守规矩的爬虫要不要来抓,完全不影响人类用户、浏览器、以及任何不守规矩的脚本。这一条衍生出三个很实际的后果。
第一,不要用它保护敏感目录。常见的误解是把 /admin/、/backup/、/tmp/ 写进 Disallow 就安全了。恰恰相反,把路径写进 robots.txt 等于在公开文件里列出「这里有好东西」,历史上大量备份文件、临时导出、调试接口就是这样被打包下载的。真正的保密手段是访问控制:Nginx 的 auth_basic、来源 IP 白名单、或者干脆把文件挪到 web 根目录之外。
第二,Disallow 不等于不收录。如果一个页面被别的站链接了,搜索引擎可以在完全不抓取页面的情况下收录它的 URL——只有「网址」没有「内容」,搜索结果里就是这么一条光秃秃的链接。这也是为什么「先 Disallow 掉再删页面」是错的操作顺序:正确顺序是先让页面返回 410 或 404,等确认已经从索引里掉出去,再考虑是否要改 robots。反过来,如果页面已经要下线,也绝不该指望 robots 帮你去收录。
第三,robots.txt 本身必须能稳定返回 200。如果它返回 5xx,守规矩的爬虫会把这个状态当「暂时不确定」处理,普遍策略是短时间内降低对整站的抓取强度甚至暂停;如果它返回 404,历史上爬虫会当作「没有限制」而抓全站。两种都不是你想要的。所以 robots.txt 放在最外层、不依赖应用、不做重定向,是最省心的做法。
二、作用域、通配符与最长匹配优先
robots.txt 的规则是按路径前缀匹配,不是按域名匹配,而且一个路径的规则里最长的那条优先,长度相同时 Allow 优先于 Disallow。这几条规则决定了同一批规则在不同写法下结果完全不同。
最常见的需求是「去掉参数化的搜索结果页,但保留正常的文章页」。搜索结果页通常是 /search?q=xxx 这种形式,写法上很多人会写成:
User-agent: *
Disallow: /search
这条规则匹配所有以 /search 开头的路径,方向是对的。但如果你想放行其中的某一类,比如 /search/tag/ 这种由标签生成的可索引聚合页,就必须显式写 Allow,并且要保证 Allow 的路径更长:
User-agent: *
Disallow: /search
Allow: /search/tag/
Disallow: /*?s=
Disallow: /*?replytocom=
Disallow: /*?orderby=
Disallow: /*&orderby=
这里的 /*?s= 是用通配符干掉站内搜索参数,/*&orderby= 是处理已有参数后又追加排序参数的情况——只写 ?orderby= 会漏掉第二、第三个参数,这是很典型的一处遗漏。参数串越长,需要收口的组合越多,所以更好的解法往往不是不断增加规则,而是在 robots 里一刀切掉参数抓取,再在页面上用 canonical 标签告诉引擎「这个参数页的正身是谁」。
另一处常被忽略的细节:路径区分大小写,/Search 和 /search 是两条不同的路径;而 robots.txt 里的注释用 #,中文注释在部分早期解析器里可能有问题,保守起见注释都用英文或干脆不写注释。
三、分爬虫策略:哪些该给,哪些不该给
通配的 User-agent: * 是兜底规则,但搜索引擎爬虫、AI 训练爬虫、以及各种数据采集器的诉求差别很大,放在同一条规则下往往会互相打架。
一个比较容易忽略的点是:主流搜索引擎的爬虫会同时读取通配规则和自己专属的规则块。专属块可以覆盖通配块里的一部分,但并不是「写了专属块就完全忽略通配块」。所以如果你给某个爬虫写了一条较短的规则,而通配块里还有一条更长的 Disallow 匹配到同一路径,结果可能是它依然被挡住。排查这类问题最直接的办法是看日志里该爬虫实际抓了哪些 URL。
对于 AI 训练类爬虫,现在的站点一般有三种态度:完全放行(换一点曝光)、只允许摘要类爬虫不允许训练爬虫、全部禁止。这属于站点策略选择,没有对错,但要注意两点:一是这类爬虫的 User-agent 名称更新很频繁,写在 robots 里的名单需要定期复核;二是这类爬虫往往不遵守 robots,写规则更多是表态而非技术封堵,真的不想被采,得在 Nginx 层按 UA 或行为特征拒绝。
还有一个实用做法是把「不该抓的」和「没时间抓的」区分开。Crawl-delay 只有部分爬虫认,主流搜索引擎基本不认,想限速应该用服务器端限流,而不是写在 robots 里自我安慰。
五、三次真实故障,以及它们各自暴露的问题
规则本身不难理解,难的是出问题时你往往不会怀疑到它头上。下面三个案例都是「现象看起来完全是别的原因」的类型。
第一次是整站从搜索结果里消失。运维同事为了挡住一个疯狂的采集器,在 robots.txt 里加了一段专属块,手滑把 User-agent: * 下面那行 Disallow: /admin/ 的注释位置写错,导致通配块里多出一行 Disallow: /。表现是:站点访问完全正常,服务器日志里爬虫请求骤减,站长后台显示「已抓取 0 个页面」。排查顺序当时是先从服务器性能、再从防火墙、再怀疑被惩罚,折腾了一整天才想起去看 robots。教训是:任何导致爬虫行为突变的改动,第一件事是 diff robots.txt,而不是先去查服务器。
第二次是禁了该禁的,却把 CSS 一起禁了。为了让图片目录不被抓,写了 Disallow: /static/,而站点把 CSS 和 JS 也放在 /static/ 下面。结果是页面能抓、内容能读,但渲染测试一直报「资源被 robots 阻止」,移动端友好性检测也挂了。这类问题的特征是:HTML 层面的抓取完全没问题,但搜索引擎对页面质量判断偏差,因为看不到样式。合理的做法是把静态资源目录和用户上传目录分开命名,robots 只针对上传目录,且放行常见的 .css、.js 扩展名。
第三次是子域遗留。主站早就换了新域名,旧子域只做了 301 跳到主站。但 robots.txt 忘了处理:旧子域的 robots 里还写着 Disallow: /,而 301 跳转本身不会让爬虫绕开 robots 去抓目标地址——它是在拿到了跳转响应之后才去请求目标。结果是主站有一批老链接的权重传导断在这里。修法很简单,旧子域直接返回 410 或把 robots 改成放行,而不是留着那份「全站禁抓」的历史文件。
六、缓存、CDN 与「改了没生效」
robots.txt 最常见的故障不是规则写错,而是改了没生效。这里有四层可能的原因,排查顺序如下。
第一层,CDN 缓存。robots.txt 一旦被 CDN 缓存,回源频率极低,改了源站也能被旧版本覆盖好几天。处理办法是在 CDN 里把 /robots.txt 设为不缓存或极短 TTL(比如 300 秒),同时按内容类型单独建一条规则。判断是否命中缓存很简单:对比 curl -sI https://example.com/robots.txt 的 x-cache 类响应头,和直接回源访问的结果。
第二层,爬虫自己的缓存。搜索引擎对 robots.txt 有独立的缓存周期,通常比页面长得多。所谓「我改完它还在抓被禁的路径」,一半是它还没重新拉取 robots。在站长后台里普遍有「重新抓取 robots.txt」或「测试 robots.txt」的入口,先用工具确认引擎看到的是新版,再去推断其他原因。
第三层,多域名与多协议。robots.txt 是 per-host 的:http 与 https、www 与裸域、以及每一个子域,各自有独立的 robots.txt,互不继承。站点从 http 升到 https、或者从裸域切到 www,如果只在一个上面维护 robots,另一个会长期停留在旧内容。用一条命令把各入口全查一遍最稳妥:
git grep -n "Disallow" -- robots.txt 2>/dev/null
for u in http://example.com https://example.com https://www.example.com; do
echo "== $u"
curl -s -o /dev/null -w '%{http_code} %{redirect_url}' "$u/robots.txt"
echo
done第四层,服务器层的路径处理。Nginx 里如果有正则 location 逻辑(比如把 .txt 当作静态资源做了额外的 rewrite,或者把所有不存在的路径都 index.php 兜底),robots.txt 可能被重写到一个返回 200 但内容是首页 HTML 的地址上。这时爬虫拿到的是「一份 HTML 格式的 robots.txt」,解析结果当然全是错的。验证方式是看 Content-Type:必须是 text/plain,且首行开始就是 User-agent: 而不是 <!DOCTYPE html>。
七、和 sitemap 联动,以及一份可执行的核对清单
robots.txt 里最后一行通常可以放 Sitemap:,这是把「抓取许可」和「抓取入口」写在一起的最省事方式。注意三点:Sitemap 之后的 URL 必须是绝对地址;一行只能写一个 sitemap;分片后的 sitemap 索引文件只需要写索引那一个。
User-agent: *
Disallow: /admin/
Disallow: /search
Allow: /search/tag/
Disallow: /*?s=
Disallow: /*?replytocom=
Crawl-delay: 1
Sitemap: https://www.example.com/sitemap.xml下面是我每次改完 robots 之后固定走的核对清单,五分钟能跑完,比事后猜要划算得多。
一是语法核对:每条规则只有一个 User-agent 下的若干行,组与组之间用空行分隔;指令名大小写不敏感但为了可读统一写首字母大写;路径以 / 开头;出现任何 Disallow: / 都要确认是真的想全站封杀——这一条写错一次,整站从索引里消失不是夸张。
二是新旧版本对比:改动前把当前线上版本存一份,改完 diff 一遍。我在一次事故里就是因为顺手删掉了一行 Allow: /*.js$,导致 JS 与 CSS 全部被禁,页面渲染异常,而排查时一直在看 HTML。
三是抽样验证:从日志里挑出被禁的路径和应放行的路径各三条,用 curl -s https://example.com/robots.txt 加上搜索后台的 robots 测试工具,确认判定结果和预期一致。不要只信自己读规则读出来的结论。
四是记录变更时间:在文件头写一行注释记录改动日期和原因,或者干脆把它纳入版本库。robots.txt 的问题往往在改动后一两周才暴露,能回溯到「什么时候改了什么」比什么都重要。
最后提醒一句:robots.txt 是给爬虫看的礼貌请求,不是站点安全的边界。真正需要隐藏的路径,靠 Nginx 的 deny、认证或目录隔离;robots 只负责告诉守规矩的爬虫「这一块不用浪费时间」。把这两件事分清,大部分关于收录和不收录的困惑都会自动消失。