robots.txt 不生效:先分清是哪一层缓存

改了 robots.txt 但 Google 还在爬同一批 URL,几乎都是缓存或重复文件的问题。本文用一条 curl 帮你定位是 CDN 缓存、Google 自己的 24 小时缓存,还是项目里有两份 robots.txt 互相覆盖。

你在 public/robots.txtsrc/pages/robots.txt.ts 里加了一条 Disallow: /admin/,部署后 Search Console 的 robots.txt 报告看到的还是上一版本,Googlebot 也在继续抓 /admin/。这通常不是语法错。

最快的判断: 跑一条 curl -s "https://yourdomain.com/robots.txt?cb=$(date +%s)"。如果这条带 cache buster 的请求显示的是你的新规则,说明源站没问题,旧版本来自某一层缓存——单独清掉 CDN 里这一个文件,再等 Google 自己的 24 小时缓存过期即可。如果带 buster 的请求显示的还是旧规则,那是部署根本没把新文件发出去,多半是残留的 public/robots.txt 把动态路由覆盖了。

为什么会这样:Google 缓存 robots.txt 最长 24 小时(遇到 5xx 或超时还会更久),CDN 又叠了一层缓存,而且很多项目里其实有两份 robots.txt 在互相覆盖。本文按命中率把它拆成 5 类原因,每类都给一条可以直接 curl 或在 Search Console 里验证的判断方式。

你属于哪一种情况

先把”最短修复路径”Step 1 的两条 curl 跑一遍,再对照下表。

带 buster 的 curl 显示不带 buster 的 curl 显示最可能的原因跳到
新规则旧规则CDN 边缘缓存原因 2,Step 2
新规则新规则Google 自己的 24h 缓存原因 1,Step 4
旧规则旧规则重复文件,或部署没发出去原因 3,Step 3
新规则但 URL 仍被收录Disallow 无法去索引原因 4,Step 4
content-type 是 text/html 或非 200路由错了,或返回的是错误页原因 5

常见原因

按命中率从高到低。

1. Google 的 robots.txt 缓存还没过期

Google 官方文档明确说会缓存 robots.txt 最长 24 小时,遇到超时或 5xx 无法刷新时还会缓存更久。你刚改完,Googlebot 用的还是缓存里那一份。Google 也正因如此建议不要一天内反复改 robots.txt——在缓存换新之前,你那些频繁改动它根本看不到。

如何判断:Search Console → 设置 → robots.txt 报告,看 “Checked on” 时间,那是 Google 最近一次抓取该文件的时刻。如果是几小时前抓的、内容是旧版,就是缓存。

2. CDN 给 robots.txt 加了 edge cache

Cloudflare、Vercel Edge 默认对 .txt 文件按 Cache-Control 头缓存,常见 4 小时甚至 24 小时。源站是对的,但 Googlebot 从 CDN 边缘拉到的还是旧版。

如何判断

curl -I "https://yourdomain.com/robots.txt"

看响应头里 cf-cache-status / x-vercel-cacheHIT 说明这次是从缓存返回的。age 字段告诉你这份缓存已经放了多少秒。

3. 同时存在两份 robots.txt,静态优先

最坑的一种:你在 Astro / Next 里写了动态路由 src/pages/robots.txt.ts 生成 robots,但 public/robots.txt 里还有一份历史文件没删。绝大多数框架对 public/ 里的静态文件优先输出,动态路由被悄悄覆盖。

如何判断

ls public/robots.txt src/pages/robots.txt* 2>/dev/null

如果两个都存在,问题就是它:静态的 public/robots.txt 赢了,你的动态路由根本没跑。

4. 用 Disallow 想达到 noindex 的效果

Disallow: /private/ 只是阻止抓取,不阻止索引。如果别处有外链指向那个 URL,Google 仍可能收录(搜索结果里显示 “No information is available for this page”)。你以为 robots.txt 失败了,其实它做了它该做的——只是你需要的是另一个工具。

如何判断:在 Search Console → URL 检查器(URL Inspection)里输入该 URL。如果 “Crawl allowed?” 是 No、但页面仍在索引里,说明 robots 生效了,你真正需要的是 noindex

5. 大小写、字段名或 content-type 错了

disallow: 必须是 Disallow:(首字母大写)。User-agent: 不能写成 User-Agents:。通配符规则有放置要求——Google 支持 *$,但像 Disallow: */admin/ 这种写法会被宽松处理,部分非 Google 的 bot 干脆完全忽略通配符。更常见的一种翻车:你的路由把规则当成 text/html(或一个 404 页面)返回,而 Google 只认 HTTP 200 + text/plain 正文。

如何判断:Google 已在 2023 年底 / 2024 年下线了独立的 robots.txt 测试工具(tester),Console 里不再有内置测试器。改用以下方式:把具体路径粘到 URL Inspection 看是否被屏蔽;看 robots.txt 报告里列出的 warnings;或者用 Google 的开源 robots.txt 解析器在本地离线校验——那正是 Googlebot 自己用的那套库。

最短修复路径

按收益从高到低,前 3 步通常就能解决 80% 的问题。

Step 1:用 curl + cache buster 拿到真正的源站响应

curl -I "https://yourdomain.com/robots.txt?cb=$(date +%s)"
curl -s "https://yourdomain.com/robots.txt?cb=$(date +%s)"

第一条看响应头,第二条看内容。判断点:

  • 状态码必须 200(不是 301 / 404
  • content-type 必须是 text/plain(不能是 text/html——HTML 正文会被 Google 忽略)
  • cf-cache-status / x-vercel-cache 应当是 MISSDYNAMIC(buster 强制重新拉取)
  • 内容是你最新版本

把结果对照上面的”你属于哪一种情况”表。简单说:带 buster 是新版、不带是旧版 → 缓存问题(走 Step 2);带 buster 也是旧版 → 部署根本没发出这个文件(走 Step 3)。

Step 2:只清 /robots.txt 这一个文件的 CDN 缓存

不要 purge everything——那会无谓地冷启动你整站的缓存。

  • Cloudflare:Caching → Configuration → Purge Cache → Custom Purge → “Purge by: URL”,粘 https://yourdomain.com/robots.txt。要用完整的 UTF-8 URL,单文件 purge 不支持通配符。
  • Vercel:每次新的生产部署会自动清空 edge cache,所以在项目根目录跑 vercel --prod --force(顺带绕过 build cache),或在 dashboard 点 Redeploy。想重新部署某个已有构建,用 vercel redeploy <deployment-url> --target production
  • Netlify:Deploys → Trigger deploy → Clear cache and deploy site

清完再用 Step 1 不带 buster 的 curl 验证 cf-cache-status: MISS(下次请求会再变回 HIT),并确认内容已更新。

Step 3:确保只有一个 robots.txt 来源

# 删掉 public/robots.txt,只保留动态路由(或反过来)
rm public/robots.txt
# 或:删掉动态路由
rm src/pages/robots.txt.ts

二选一。然后重新 build + 部署:

npm run build
ls dist/robots.txt && head -20 dist/robots.txt

确认 dist/robots.txt 是你想要的那份。(Next.js App Router 下动态文件是 app/robots.ts,规则一样:public/robots.txt 仍然会盖过它。)

Step 4:让 Google 立刻重抓

到 Search Console → 设置 → robots.txt 报告 → 点文件旁边的 more settings(三个点)图标 → Request a recrawl。“Checked on” 时间通常几分钟到一两小时内更新;Google 把这条路径定位为应急通道,对应平时约 24 小时的自动刷新。

如果你想完全去索引(不只是禁止抓取),别在 robots.txt 里 Disallow,改用页面级 meta:

<meta name="robots" content="noindex">

或者在 HTTP 响应头里(PDF 等非 HTML 文件更适合用这种):

X-Robots-Tag: noindex

robots.txt 的 Disallow 实际上会阻止 Google 抓取页面,从而根本看不到那个 noindex 标签,所以两者用法是相反的。要去索引,应当让 URL 保持可抓取、加上 noindex、等它重抓一次,确实想连抓取一起挡时再补 Disallow

怎么确认修好了

  1. 不带 buster 的 curl -s https://yourdomain.com/robots.txt 返回新规则,200 + text/plain,且 cf-cache-status: MISS(或 age 很小)。
  2. Search Console 的 robots.txt 报告里 “Checked on” 是近期时间,显示的内容和你的新文件一致。
  3. 对被屏蔽的路径,URL Inspection 显示 “Crawl allowed? No”。对要去索引的路径,显示 “Indexing allowed? No”,并在下次抓取后从 Pages → Indexed 报告里掉出去。

预防建议

  • 把 robots.txt 的”来源”写进 README,让团队所有人清楚是 public/ 还是动态路由,绝不能两份都有。
  • 部署前用 Google 的开源 robots.txt 解析器校验改动——Console 里那个内置 tester 已经没了。
  • 想去索引就用 noindex meta / X-Robots-Tag header,不要用 Disallow
  • 部署后加一条冒烟测试,curl /robots.txt 并断言 200 + text/plain + 最新内容。
  • 给 robots.txt 设较短的 Cache-Control(如 max-age=600);Google 会参考较低的 max-age,CDN 持有旧版的时间也会更短。

常见问题

改完 robots.txt,Google 多久才会照新的执行? 靠它自己最长 24 小时;在 robots.txt 报告里点 “Request a recrawl” 则通常几分钟到一两小时。CDN 缓存是另一回事,可能再叠几小时——把它清掉(Step 2),Googlebot 下次来才能拉到新版。

我在 robots.txt 里屏蔽了某个 URL,它却还在 Google 索引里,为什么? Disallow 拦的是抓取,不是索引。Google 已经知道、或被别站外链指向的页面,仍可能带着 “No information is available” 摘要留在索引里。要移除,得先放开抓取再加 noindex(Step 4);继续屏蔽抓取反而会让 Google 看不到那个 noindex

curl 已经是新文件,但 Googlebot 还在用旧的,怎么办? 说明源站和 CDN 都对了,这是 Google 自己的 24 小时缓存。在 robots.txt 报告里 Request a recrawl,然后等。没有更快的官方手段。

AllowDisallow 规则的先后顺序有影响吗? 对 Googlebot 没有——它按最具体(路径最长)的规则匹配,与书写顺序无关。所以对 /admin/public/page 来说,Allow: /admin/public/ 会胜过 Disallow: /admin/。但有些爬虫用”首条匹配”,所以规则尽量写得不产生歧义。

为什么我的动态 robots.txt.ts 路由被忽略了? 静态的 public/robots.txt(Astro/Vite),或与 app/robots.ts 并存的 public/robots.txt(Next.js),会被优先输出,动态路由根本不运行。删掉静态文件(Step 3)再重新 build 即可。

能屏蔽单个 PDF 或图片不进搜索吗? Disallow 只挡抓取;要真正去索引非 HTML 文件,用 X-Robots-Tag: noindex 响应头——你没法往 PDF 里塞 meta 标签。

相关阅读

标签: #部署 / 托管 #排查 #排查 #SEO