Skip to content

fix(seo): serve a real sitemap index at /sitemap.xml#36

Merged
oratis merged 1 commit into
mainfrom
claude/sitemap-index
Jun 27, 2026
Merged

fix(seo): serve a real sitemap index at /sitemap.xml#36
oratis merged 1 commit into
mainfrom
claude/sitemap-index

Conversation

@oratis

@oratis oratis commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Follow-up to #35. generateSitemaps() served the chunked children at /sitemap/{id}.xml, but Next reserves /sitemap.xml for the sitemap.ts metadata convention and 404s the parent index there — and robots.txt + Search Console point at /sitemap.xml. (A parallel route handler can't coexist with the convention: it errors Conflicting route and metadata at /sitemap.xml.)

Fix

Drop the sitemap.ts metadata convention and hand-roll the XML:

  • /sitemap.xml (app/sitemap.xml/route.ts) → <sitemapindex> of the children
  • /sitemap/{id}.xml (app/sitemap/[id]/route.ts) → <urlset> chunk (1,000 URLs)
  • @/lib/sitemap-shape → shared chunk math + XML serialization, hreflang alternates preserved, force-dynamic so the catalog is always complete. A DB blip degrades a child to an empty <urlset> instead of a 500.

Verified locally

  • /sitemap.xml → well-formed <sitemapindex>.
  • /sitemap/0.xml → well-formed <urlset>, 29 page URLs, 435 hreflang links (29×15).
  • /sitemap/99.xml → empty urlset (200); /sitemap/abc.xml, /sitemap/-1.xml → 404.
  • No route/metadata conflict; robots.txt still points at the (now-working) /sitemap.xml.

Will confirm the index + a couple of entity children in production after deploy.

🤖 Generated with Claude Code

Follow-up to #35. generateSitemaps() served the chunked children at
/sitemap/{id}.xml but Next reserves /sitemap.xml for the metadata
convention and 404s the parent index there — so robots.txt and Search
Console (which point at /sitemap.xml) got a 404.

Drop the sitemap.ts metadata convention and hand-roll the XML instead:
- /sitemap.xml (app/sitemap.xml/route.ts) -> <sitemapindex> of the children
- /sitemap/{id}.xml (app/sitemap/[id]/route.ts) -> <urlset> chunk
- @/lib/sitemap-shape: shared chunk math + XML serialization (hreflang
  alternates preserved), force-dynamic so the catalog is always complete.

A real index can't be expressed with the sitemap.ts convention + a
parallel route handler — they collide ("Conflicting route and metadata at
/sitemap.xml"), so hand-rolling is required.

Verified on a local dev server: /sitemap.xml is a well-formed sitemapindex;
/sitemap/0.xml is a well-formed urlset with 29 page URLs and 435 hreflang
links; bad/out-of-range ids 404 / return an empty urlset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@oratis oratis merged commit afb6026 into main Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant