Skip to content

sitemap: route.ts handler with explicit Cache-Control (fix P0 leftover) - #1483

Merged
Flotapponnier merged 1 commit into
devfrom
feat/sitemap-route-handler
Jul 26, 2026
Merged

sitemap: route.ts handler with explicit Cache-Control (fix P0 leftover)#1483
Flotapponnier merged 1 commit into
devfrom
feat/sitemap-route-handler

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Follow-up to #1473

The #1473 attempt to set /sitemap.xml Cache-Control via next.config headers() was silently ignored — Next Metadata routes emit their own headers that override the config layer. Post-deploy verification (2026-07-26 03:30 UTC) showed sitemap still returned public, max-age=0, must-revalidate with x-vercel-cache: MISS.

Fix

Convert to a Route Handler that returns a raw Response with the header we want:

  • Move builder logic from src/app/sitemap.ts to src/lib/sitemap-builder.ts (rename default export → buildSitemap)
  • New src/app/sitemap.xml/route.ts serializes to XML + sets Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400
  • Drop the now-redundant /sitemap.xml entry from next.config.ts headers()

Same builder logic, same fallback path, same content. Only the transport changes.

Test plan

  • pnpm typecheck
  • After deploy: curl -sI https://openchainbench.com/sitemap.xml shows s-maxage=3600, stale-while-revalidate=86400 and x-vercel-cache: HIT on the 2nd request

Next Metadata routes ignore next.config.ts headers() — the previous
sitemap.ts left Next in charge of the response headers and hard-coded
Cache-Control: public, max-age=0, must-revalidate on force-dynamic
sitemaps. Every crawler hit re-ran the full loader chain (x-vercel-cache:
MISS observed on prod 2026-07-26 after PR #1473 landed the config-level
attempt). Confirmed by curl: my next.config /sitemap.xml rule was
silently dropped.

Move the builder logic to src/lib/sitemap-builder.ts and add a
Route Handler at src/app/sitemap.xml/route.ts that returns a raw
Response with:
  Content-Type: application/xml
  Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400

Same builder logic, same buildFullSitemap + buildStaticFallback,
just serialized to XML in the route and shipped with a real edge
cache header. Also drop the now-redundant /sitemap.xml rule from
next.config headers().
@Flotapponnier
Flotapponnier merged commit 1b8144d into dev Jul 26, 2026
1 check failed
Flotapponnier added a commit that referenced this pull request Jul 26, 2026
Next Metadata routes ignore next.config.ts headers() — the previous
sitemap.ts left Next in charge of the response headers and hard-coded
Cache-Control: public, max-age=0, must-revalidate on force-dynamic
sitemaps. Every crawler hit re-ran the full loader chain (x-vercel-cache:
MISS observed on prod 2026-07-26 after PR #1473 landed the config-level
attempt). Confirmed by curl: my next.config /sitemap.xml rule was
silently dropped.

Move the builder logic to src/lib/sitemap-builder.ts and add a
Route Handler at src/app/sitemap.xml/route.ts that returns a raw
Response with:
  Content-Type: application/xml
  Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400

Same builder logic, same buildFullSitemap + buildStaticFallback,
just serialized to XML in the route and shipped with a real edge
cache header. Also drop the now-redundant /sitemap.xml rule from
next.config headers().

Co-authored-by: Florent Tapponnier <contact@mobula.io>
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