sitemap.xml 找不到(404):5 个原因 + Astro 修复路径

/sitemap.xml 或 /sitemap-index.xml 返回 404,或 Search Console 显示 'Couldn't fetch'。按命中率排的 5 个原因,每个都能直接在 dist/ 验证。

你在 Google Search Console 提交 https://yourdomain.com/sitemap.xml,状态显示 Couldn't fetch404。这是 Astro 站点最常见的首次部署 bug(Next / Hugo 也会因为同样的原因中招)。最可能的单一原因:@astrojs/sitemap 装了但没在 astro.config.mjs 里注册,于是 build 静默地没往 dist/ 里写 sitemap。第二常见:site 没配,integration 直接跳过生成。第三常见:你提交的文件名错了——integration 产出的是 sitemap-index.xml,不是 sitemap.xml

最快修复: 打开 astro.config.mjs,确认 site: 'https://yourdomain.com' 已设置,且 sitemap()(带括号)出现在 integrations 数组里;跑 npm run build,再 ls dist/sitemap*。如果看到 sitemap-index.xml,就提交这个 URL,而不是 /sitemap.xml

本文按命中率列出 5 类原因,每个都配一条能直接在 dist/ 或线上 URL 跑的检查命令。

先判断你属于哪一类

现象最可能的原因跳转
dist/ 里完全没有 sitemap* 文件没注册 integration,或 site 没配原因 1、2
本地有 dist/sitemap-index.xml,但线上 /sitemap.xml 404提交的文件名错了原因 4
线上 URL 返回 200,但 content-type: text/html宿主 rewrite / SPA fallback 拦截了原因 3
文件跑到了 dist/blog/ 之类的前缀下base 路径偏移原因 5
文件是合法的(浏览器里能打开成 XML),但 GSC 还是 Couldn't fetchGSC 抓取延迟或缓存过期见 FAQ

常见原因

按命中率从高到低排列。

1. 没在 astro.config 注册 @astrojs/sitemap

npm install @astrojs/sitemap 不够,必须把它加进 integrations 数组:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://yourdomain.com',
  integrations: [sitemap()],
});

少了 integrations: [sitemap()] 这一行,dist/sitemap-index.xml 就不会被生成,而且 build 仍然以 0 退出——不会有任何报错提醒你。

如何判断:

grep -n "sitemap" astro.config.mjs

如果只看到 import 行、integrations 里却没有 sitemap() 调用,就是没注册。

2. site 字段为空

@astrojs/sitemapsite 来拼接每个条目的绝对 URL,所以缺了它就拒绝运行。按官方文档,site 是必填项,并且必须以 http://https:// 开头(截至 2026 年 6 月)。site 缺失时,integration 会在构建时打一条 warning(多数人直接划过去),然后整个跳过生成,dist/ 里什么都不会出现。

如何判断:npm run build,读 integration 的输出。site 缺失会产生类似 [@astrojs/sitemap] The Sitemap integration requires the site astro.config option 的 warning。看到这行就是它。

3. 宿主 rewrite 把 /sitemap* 当作未匹配路径

跟 RSS 同病——Vercel / Netlify 的 SPA fallback(如 /* -> /index.html)会吞掉 sitemap,返回你的应用外壳。纯静态 Astro 部署一般不会中招,但你如果手动改过 vercel.json 的 rewrites 或 netlify.toml 的 redirects,就要检查。

如何判断:

curl -I "https://yourdomain.com/sitemap.xml"
curl -I "https://yourdomain.com/sitemap-index.xml"

返回 200content-type: text/html(而不是 application/xml)→ SPA fallback 接管了,给你返回的是 HTML。

4. URL 用错——应该是 sitemap-index.xml,不是 sitemap.xml

@astrojs/sitemap 产出的是 sitemap-index.xml 加上一个或多个编号文件(sitemap-0.xmlsitemap-1.xml……)。每超过 45000 个 URL 就会拆出一个新文件(默认 entryLimit,截至 2026 年 6 月)。它不会产出普通的 sitemap.xml。如果你只在 Search Console 提交了 /sitemap.xml,就会拿到 404。

如何判断:

ls dist/sitemap*

如果只看到 sitemap-index.xmlsitemap-0.xml、没有普通的 sitemap.xml,那就提交 index 那个 URL。

5. base 路径偏移

如果你的 Astro 配置了 base: '/blog',sitemap 会输出到 dist/blog/sitemap-index.xml,访问 /sitemap.xml(不带 base)就是 404。

如何判断:

grep -n "base:" astro.config.mjs
find dist -name "sitemap*"

实际位置带 base 前缀就是这个原因——提交时要带上前缀(https://yourdomain.com/blog/sitemap-index.xml)。

最短修复路径

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

Step 1:把 @astrojs/sitemap 注册到 config

完整可用的最小配置:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://yourdomain.com', // 必填,且是绝对 http(s) URL
  integrations: [
    sitemap({
      filter: (page) => !page.includes('/admin/'),
      changefreq: 'weekly',
      priority: 0.7,
    }),
  ],
});

确认两点:

  • sitehttp://https:// 开头的绝对 URL,惯例上不带末尾斜杠
  • sitemap() 出现在 integrations 里且被实际调用(注意带括号)

Step 2:本地 build 验证产物

rm -rf dist/
npm run build
ls dist/sitemap*
head -20 dist/sitemap-index.xml

期望看到:

dist/sitemap-0.xml
dist/sitemap-index.xml

sitemap-index.xml 内容应该指向 https://yourdomain.com/sitemap-0.xml。如果 site 配错了,这里就会暴露——比如指向 http://localhost:4321/sitemap-0.xml,部署上去 Google 拉不到。先改对 site 再重新 build,然后才部署。

Step 3:部署后用 curl 验证线上

curl -I "https://yourdomain.com/sitemap-index.xml"
curl -s "https://yourdomain.com/sitemap-index.xml" | head -10

通过标准:

  • 状态 200
  • content-type: application/xml
  • 内容包含 <sitemapindex 标签

返回 200 但是 HTML → 有 rewrite 在拦截。去 vercel.json / netlify.toml 检查 SPA fallback,并把 /sitemap* 从中排除。

Step 4:把正确的 URL 提交到 Search Console

在 Search Console 里打开站点资源,左侧导航进 Indexing -> Sitemaps(索引 -> 站点地图)。在 “Add a new sitemap”(添加新的站点地图)处只填路径:

sitemap-index.xml

注意是带 -index 的(除非你通过 entryLimit 或自定义序列化选项拆分过 sitemap)。提交后状态应当在几分钟到几小时内变成 Success;新资源可能要等上一天。如果一直卡在 Couldn't fetch,看 FAQ——那往往是抓取延迟,不是真的失败。

同时把 sitemap 写进 robots.txt,让其他爬虫也能找到:

Sitemap: https://yourdomain.com/sitemap-index.xml

这能帮 Bing、Yandex 等自动发现。另外要确认 robots.txt 没有 Disallow 掉 sitemap 路径——Google 抓 sitemap 时会遵守 robots.txt,一条拦截规则是 Couldn't fetch 的常见成因。

Step 5:CI 部署后冒烟测试

#!/usr/bin/env bash
set -e
URL="https://yourdomain.com/sitemap-index.xml"
status=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
[[ "$status" == "200" ]] || { echo "Sitemap status: $status"; exit 1; }
curl -s "$URL" | grep -q "<sitemapindex" || { echo "Not a sitemap index"; exit 1; }
echo "Sitemap OK"

挂在部署成功 hook 上,任何 commit 不小心删了 sitemap() 调用,都会在 Search Console 看到 404 之前就被立刻发现。

如何确认已经修好

满足下面三条就算搞定:

  1. curl -I https://yourdomain.com/sitemap-index.xml 返回 200,且 content-type: application/xml
  2. 浏览器里打开这个 URL,显示的是干净的 XML,以 <?xml 开头、含 <sitemapindex> 元素(不是你站点的 HTML 外壳)。
  3. Search Console 里这条提交显示 Success,且发现的 URL 数大于 0。

如果第 1、2 条都过、Search Console 却还是 Couldn't fetch,那文件本身没问题——是 Google 侧的延迟,见 FAQ。

预防建议

  • 加一条 CI 断言:build 后 dist/sitemap-index.xml 必须存在;或者部署后跑 Step 5 的冒烟测试
  • 在 Search Console 提交后,每周看一次状态,第一时间发现 URL 数下跌
  • robots.txt 里写明 Sitemap: 行,让 Bing / Yandex 也能自动发现,并确认没把该路径 disallow 掉
  • 别把 sitemap 路径硬编码到模板里,直接信任 @astrojs/sitemap 的默认输出
  • site 字段或 base 路径时,把 sitemap 测试一并放进同一个 diff 里 review

常见问题(FAQ)

Search Console 显示 Couldn't fetch,但浏览器里 URL 打得开,怎么办? 这通常是 Google 侧的抓取延迟或缓存过期,不是真错误。先确认文件可达:curl -I 应返回 200application/xml。如果是的,就把这条提交留着——Google 往往会在一两天内自己把 Couldn't fetch 翻成 Success。如果卡了好几天,删掉这条 sitemap 重新提交,并对该 sitemap URL 用 URL Inspection(网址检查) 强制重新抓取一次。如果确实是缓存卡死,最后的办法是把 sitemap 部署到一个略有不同的路径,再提交那个新路径。

为什么没有 sitemap.xml,只有 sitemap-index.xml 这是设计如此。@astrojs/sitemap 始终产出一个索引文件(sitemap-index.xml),指向一个或多个编号子文件(sitemap-0.xml,URL 超过 45000 后会有更多)。提交并链接索引 URL 即可,Google 会自动顺着它去抓子文件。

一定要把 sitemap 提交到 Search Console 吗? 不一定——在 robots.txt 里写 Sitemap: https://yourdomain.com/sitemap-index.xml,爬虫就能自己发现。但在 Search Console 里提交能给你看到发现/已索引的 URL 数,还会暴露解析错误,所以但凡你在意的站点都值得提交。

build 通过了,但 dist/ 里没有 sitemap,为什么不报错? site 缺失或没注册 sitemap() 都是非致命情况:integration 打一条 warning 后正常退出,于是 npm run build 返回 0。重新 build 一次,读输出里的 [@astrojs/sitemap] 行;或者加上 Step 5 的冒烟测试,让缺产物时 CI 大声报错。

我用的是 SSR / output: 'server',sitemap 还会生成吗? 会,但只覆盖 build 时已知的路由(你预渲染的静态页)。纯动态的 SSR 路由不会自动出现。用 integration 的 customPages 选项把动态 URL 注入进来,或单独为它们生成一份 sitemap。

相关阅读

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