fix: shard sitemap.xml per locale#18812
Conversation
Even served statically, a single sitemap document is ~51MB / 17.5k URLs -- over Google's 50MB and 50k-URL per-file limits -- so crawlers reject it outright. Split it into one shard per locale via generateSitemaps(), each ~2MB / ~700 URLs, comfortably under both limits. Each shard still emits the full hreflang alternates block so cross-language reciprocity is preserved. Next serves the shards at /sitemap/<locale>.xml and emits no index file, so robots.ts now lists every shard for crawler discovery in place of the old /sitemap.xml. A memoized traversal keeps all shards sharing a single content walk. Verified against a full 25-locale build: all shards prerender static (initialRevalidateSeconds: false, no ISR), close with </urlset>, carry ~700 URLs each including /wallets/find-wallet/, and the largest is ~2MB. Part 2 of #18014. Note: this removes the root /sitemap.xml; whatever is registered in Google Search Console should be updated to the shard URLs (robots.txt covers auto-discovery regardless). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
🔎 First-pass review — ✅ Looks mergeableSplits the sitemap into one shard per locale via Note for reviewers: base branch is Analysis
|
Summary
Part 2 of #18014, stacked on #18802 (base branch is
fix/sitemap-static, notdev— review that first). Splits the sitemap into one shard per locale so it clears Google's per-file limits.Why
Even after #18802 makes the sitemap build statically (complete, no truncation), it's still a single ~51 MB document with ~17.5k URLs and ~455k hreflang alternates — over Google's 50 MB / 50k-URL per-file limits, so crawlers reject it outright. Sharding is required for the sitemap to be accepted at all.
Changes
app/sitemap.ts:generateSitemaps()emits one shard per locale (id = locale). The defaultsitemap({ id })filters pages to that locale and keeps the full hreflang alternates block, so cross-language reciprocity is preserved. A memoized traversal lets all shards share a single content walk.app/robots.ts: lists every/sitemap/<locale>.xmlshard. Next serves shards at/sitemap/<id>.xmland emits no index file, so robots.txt is the discovery mechanism; this replaces the old single/sitemap.xmlreference.Verification (full 25-locale build)
initialRevalidateSeconds: falsein the prerender manifest — no ISR).</urlset>, carries ~700 URLs, and includes the sentinel/wallets/find-wallet/(the page that was invisible to crawlers in prod).pt-br/zh-tw/en) — well under 50 MB, ~700 URLs each vs the 50k cap.Post-merge follow-up (not code)
This removes the root
/sitemap.xml. robots.txt handles crawler auto-discovery, but the sitemap registered in Google Search Console points at/sitemap.xmland should be updated to the shard URLs so GSC stops hitting a 404. Nothing in the codebase depends on the old path.A CI regression guard (parse each shard: valid XML, closing
</urlset>, URL-count floor, sentinel late-alphabet URL) will follow as a separate PR — this is the check that was missing when #17718 was closed on a single lucky fetch.-- Opus 4.8