AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Metadata Routes (Sitemap.Ts, Robots.Ts) Send Incorrect Cache-Control Header Instead Of S-Maxage Derived From Revalidate

Next.js metadata routes such as sitemap and robots are prerendered and respect revalidate, but their Cache-Control header is hardcoded to 'public, max-age=0, must-revalidate' instead of using s-maxage like other ISR routes. This prevents CDNs from caching these routes efficiently, especially in self-hosted environments.

mediumConfidence 95%Next.jsAffected V16.3.8Affected V16.4.0-Canary.61

Origin Analysis

The metadata route generation code (both webpack loader and Turbopack) hardcodes the Cache-Control header to 'public, max-age=0, must-revalidate' rather than deriving it from the route's revalidate and expire values using the standard getCacheControlHeader utility.
1. Create a Next.js app with a sitemap.ts or robots.ts that exports a revalidate value. 2. Build and start the app. 3. Request the metadata route (e.g., /sitemap.xml) and inspect the Cache-Control header. 4. Observe that it is 'public, max-age=0, must-revalidate' instead of 's-maxage=<revalidate>, stale-while-revalidate=<expire-revalidate>'.

Fixing Code Block

// In packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts // Add import at the top of the file: import { getCacheControlHeader } from 'next/dist/server/lib/cache-control'; // In the generated code where the response headers are set, replace the hardcoded line: // Before: 'Cache-Control': 'public, max-age=0, must-revalidate' // After: 'Cache-Control': getCacheControlHeader({ revalidate: mod.revalidate, expire: mod.expire }) // For Turbopack, a similar change is required in crates/next-core/src/next_app/metadata/route.rs to compute the header from revalidate/expire instead of using a fixed string.
The fix replaces the hardcoded Cache-Control header with a call to getCacheControlHeader, passing the route's revalidate and expire values. This ensures that metadata routes receive the same cache headers as other ISR routes, enabling CDNs to cache them according to the configured revalidation interval.

Edge Case Audit

This change may alter caching behavior for metadata routes; if any downstream system relies on the old 'max-age=0' header to force revalidation, it may now serve stale content until the revalidate interval expires. However, this is the intended ISR behavior. The getCacheControlHeader utility is internal and could change in future Next.js versions; the fix should be synchronized across webpack and Turbopack to avoid inconsistencies. Rollback: revert the patch and use the headers() workaround in next.config.ts to manually set the desired Cache-Control for specific paths.

Ecosystem Topology