#15579 08437d5 Thanks @ascorbic! - Adds two new experimental flags for a Route Caching API and further configuration-level Route Rules for controlling SSR response caching.
Route caching gives you a platform-agnostic way to cache server-rendered responses, based on web standard cache headers. You set caching directives in your routes using Astro.cache (in .astro pages) or context.cache (in API routes and middleware), and Astro translates them into the appropriate headers or runtime behavior depending on your adapter. You can also define cache rules for routes declaratively in your config using experimental.routeRules, without modifying route code.
This feature requires on-demand rendering. Prerendered pages are already static and do not use route caching.
Getting started
Enable the feature by configuring experimental.cache with a cache provider in your Astro config:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
import { memoryCache } from 'astro/config';
export default defineConfig({
adapter: node({ mode: 'standalone' }),
experimental: {
cache: {
provider: memoryCache(),
},
},
});
Using Astro.cache and context.cache
In .astro pages, use Astro.cache.set() to control caching:
---
// src/pages/index.astro
Astro.cache.set({
maxAge: 120, // Cache for 2 minutes
swr: 60, // Serve stale for 1 minute while revalidating
tags: ['home'], // Tag for targeted invalidation
});
---
<html><body>Cached page</body></html>
In API routes and middleware, use context.cache:
// src/pages/api/data.ts
export function GET(context) {
context.cache.set({
maxAge: 300,
tags: ['api', 'data'],
});
return Response.json({ ok: true });
}
Cache options
cache.set() accepts the following options:
-
maxAge (number): Time in seconds the response is considered fresh.
-
swr (number): Stale-while-revalidate window in seconds. During this window, stale content is served while a fresh response is generated in the background.
-
tags (string[]): Cache tags for targeted invalidation. Tags accumulate across multiple set() calls within a request.
-
lastModified (Date): When multiple set() calls provide lastModified, the most recent date wins.
-
etag (string): Entity tag for conditional requests.
Call cache.set(false) to explicitly opt out of caching for a request.
Multiple calls to cache.set() within a single request are merged: scalar values use last-write-wins, lastModified uses most-recent-wins, and tags accumulate.
Invalidation
Purge cached entries by tag or path using cache.invalidate():
// Invalidate all entries tagged 'data'
await context.cache.invalidate({ tags: ['data'] });
// Invalidate a specific path
await context.cache.invalidate({ path: '/api/data' });
Config-level route rules
Use experimental.routeRules to set default cache options for routes without modifying route code. Supports Nitro-style shortcuts for ergonomic configuration:
import { memoryCache } from 'astro/config';
export default defineConfig({
experimental: {
cache: {
provider: memoryCache(),
},
routeRules: {
// Shortcut form (Nitro-style)
'/api/*': { swr: 600 },
// Full form with nested cache
'/products/*': { cache: { maxAge: 3600, tags: ['products'] } },
},
},
});
Route patterns support static paths, dynamic parameters ([slug]), and rest parameters ([...path]). Per-route cache.set() calls merge with (and can override) the config-level defaults.
You can also read the current cache state via cache.options:
const { maxAge, swr, tags } = context.cache.options;
Cache providers
Cache behavior is determined by the configured cache provider. There are two types:
- CDN providers set response headers (e.g. CDN-Cache-Control, Cache-Tag) and let the CDN handle caching. Astro strips these headers before sending the response to the client.
- Runtime providers implement onRequest() to intercept and cache responses in-process, adding an X-Astro-Cache header (HIT/MISS/STALE) for observability.
Built-in memory cache provider
Astro includes a built-in, in-memory LRU runtime cache provider. Import memoryCache from astro/config to configure it.
Features:
- In-memory LRU cache with configurable max entries (default: 1000)
- Stale-while-revalidate support
- Tag-based and path-based invalidation
- X-Astro-Cache response header: HIT, MISS, or STALE
- Query parameter sorting for better hit rates (?b=2&a=1 and ?a=1&b=2 hit the same entry)
- Common tracking parameters (utm_*, fbclid, gclid, etc.) excluded from cache keys by default
- Vary header support β responses that set Vary automatically get separate cache entries per variant
- Configurable query parameter filtering via query.exclude (glob patterns) and query.include (allowlist)
For more information on enabling and using this feature in your project, see the Experimental Route Caching docs.
For a complete overview and to give feedback on this experimental API, see the Route Caching RFC.