你从内容集合一把生成了 sitemap.xml。文件 18 MB,7.3 万条 URL。Search Console 要么报 “Couldn’t fetch”,要么显示 “Discovered URLs: 50,000”——刚好卡在上限,剩下的悄悄丢了。索引位置 5 万之后的页面,再也不会从 sitemap 路径被爬。Sitemaps 协议规定单文件上限 5 万 URL 和未压缩 50 MB,Google 这两个都严格执行。
修法是用一个 sitemap index 文件指向多个按段分的子 sitemap。下面讲怎么切、怎么命名、怎么验证。
常见原因
1. 单 sitemap 生成器,没分块
构建脚本不管多少都写进一个 sitemap.xml。内容量过 5 万之前都没事。
怎么判断:wc -l public/sitemap.xml。超过 ~50,003 行(头 + 5 万条 + 尾)就触线了。
2. URL 不到 5 万但未压缩超过 50 MB
每条 URL 有长 <loc>、<lastmod>、双语站的多个 <xhtml:link rel="alternate" hreflang>、<image:image> 块。2.5 万条都可能撞 50 MB。
怎么判断:ls -lh public/sitemap.xml。过 50 MB 不管多少 URL 都得切。
3. Hreflang 把文件撑得很大
en 和 zh 的双语站,每个 URL 加 2-3 个 xhtml:link。3 万文章的站翻倍到约 6 万条 URL(每种语言一个独立 <url>),每条还更大。
怎么判断:grep -c '<url>' public/sitemap.xml,跟实际页面数对比。如果是 2-3 倍,hreflang 或者重复把文件撑大了。
4. Sitemap index 指向自己或者漏了
你做了 sitemap index 但只列了一个子 sitemap(还是原来那个 7.3 万的),或者不小心把 index 文件列在了自己里面。
怎么判断:cat public/sitemap-index.xml。应该是 <sitemapindex> 里有多个 <sitemap><loc> 子项指向不同的子文件,每个子文件 URL 数都低于 5 万。
5. 按字母而不是按数量切
简单粗暴:按 URL slug 首字母切 sitemap-a.xml、sitemap-b.xml。如果 30 万 URL 里有 8 万都以 “p” 开头,那个文件还是超。
怎么判断:wc -l public/sitemap-*.xml。任何单文件 > 50,003 行说明切分逻辑错了。
6. 压缩后小于 50 MB 但解压后超
Google 看的是未压缩大小。8 MB 的 .gz 解压到 80 MB 也会失败。
怎么判断:gzip -l public/sitemap.xml.gz 看压缩前后字节数。
7. Sitemap 文件跟 robots.txt 声明不一致
你切成了 sitemap-1.xml、sitemap-2.xml,但 robots.txt 还是写 Sitemap: https://example.com/sitemap.xml。Search Console 不会自动发现 index。
怎么判断:curl https://yoursite.com/robots.txt | grep -i sitemap。应该列 sitemap-index 的 URL。
最短修复路径
第 1 步:定切分大小和方案
保守目标:每文件 2.5 万 URL 或 25 MB(硬上限的一半)。按内容类型分,方便人 debug:
sitemap-articles-1.xml…sitemap-articles-N.xmlsitemap-categories.xmlsitemap-tags.xmlsitemap-pages.xml(静态页)
第 2 步:分块生成子 sitemap
// scripts/generate-sitemaps.mjs
import fs from 'node:fs';
const CHUNK = 25000;
const articles = JSON.parse(fs.readFileSync('articles.json', 'utf8'));
const total = articles.length;
const numFiles = Math.ceil(total / CHUNK);
for (let i = 0; i < numFiles; i++) {
const chunk = articles.slice(i * CHUNK, (i + 1) * CHUNK);
const urls = chunk.map(a => `<url><loc>https://example.com/articles/${a.slug}/</loc><lastmod>${a.modifiedAt}</lastmod></url>`);
fs.writeFileSync(
`public/sitemap-articles-${i + 1}.xml`,
`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${urls.join('')}</urlset>`
);
}
第 3 步:生成 sitemap index
const indexEntries = [];
for (let i = 1; i <= numFiles; i++) {
indexEntries.push(`<sitemap><loc>https://example.com/sitemap-articles-${i}.xml</loc><lastmod>${new Date().toISOString()}</lastmod></sitemap>`);
}
indexEntries.push(`<sitemap><loc>https://example.com/sitemap-categories.xml</loc></sitemap>`);
fs.writeFileSync(
'public/sitemap.xml',
`<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${indexEntries.join('')}</sitemapindex>`
);
顶层文件还叫 sitemap.xml,原有 robots.txt 引用不用改——只是它现在是 index 不是 urlset。
第 4 步:更新 robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
完事——同一个 URL 现在指向 index 文件,列出所有子项。
第 5 步:每个文件都验证
for f in public/sitemap*.xml; do
echo -n "$f: "
grep -c '<url>\|<sitemap>' "$f"
xmllint --noout "$f" && echo "valid"
done
每个子 sitemap <url> 项不超过 5 万。Index 文件里是 <sitemap> 项(不是 <url>)。
第 6 步:在 Search Console 重新提交
Search Console → Sitemaps → 删掉原来那个单 sitemap 项,提交 https://example.com/sitemap.xml(index)。Google 会自动发现并处理子文件。
第 7 步:观察处理进度
Search Console → Sitemaps → 点每个子 sitemap。看 “Discovered URLs” 几天内涨上来。任何子项报 “Couldn’t fetch”,去查那个文件的 HTTP status 和 XML 是否合法。
哪些情况可能不是你操作错了
URL 不到 5 万的站,切分帮不上忙。这个上限只在规模上去之后才重要。5 千 URL 的站别过度工程。
容易误判的情况
误判为 crawl-budget 问题。Crawl budget 这个概念确实存在,但主要影响百万级 URL 的大站。5 万每 sitemap 的硬上限是更明确、更简单的问题,先修这个。
预防建议
- 生成 sitemap 时硬性分块(比如每文件 2.5 万 URL)。
- CI 里部署前校验 XML。
robots.txt永远指向一个 canonical 的 sitemap-index URL。- 构建日志里记 sitemap 文件大小;超 40 MB 或 4 万 URL 报警。
- 传输用 gzip 但要核未压缩大小是否撞 50 MB 上限。
FAQ
- 可不可以不用 index、直接分别提交多个 sitemap? 可以,但 index 是标准、维护更省事。
- Bing 也是 5 万上限吗? 是的——5 万 / 50 MB 是 Sitemaps 协议官方上限,主流搜索引擎都遵守。