تخطَّ إلى المحتوى

Experimental route caching

هذا المحتوى غير متوفر بلغتك بعد.

Type: object
Default: undefined

أُضيفت في: astro@6.0.0 جديد

Enables a platform-agnostic API for caching responses from on-demand rendered pages and endpoints. Cache directives set in your routes are translated into the appropriate headers or runtime behavior depending on your configured cache provider.

Route caching builds on standard HTTP caching semantics, including max-age and stale-while-revalidate, with support for tag-based and path-based invalidation, config-level route rules, and pluggable cache providers that adapters can set automatically.

This feature requires on-demand rendering. Prerendered pages are already static and do not use route caching.

To enable this feature, configure experimental.cache with a cache provider in your Astro config:

astro.config.mjs
import { defineConfig, memoryCache } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
adapter: node({ mode: 'standalone' }),
experimental: {
cache: {
provider: memoryCache(),
},
},
});

Use Astro.cache in .astro pages or context.cache in API routes and middleware to control caching per request. Cache defaults for groups of routes can also be defined declaratively in your config using experimental.routeRules.

Use cache.enabled to check whether a cache provider is configured and active. This returns false when no provider is configured, or in development mode:

src/pages/products/[id].astro
---
if (Astro.cache.enabled) {
const tags = await getProductTags(Astro.params.id);
Astro.cache.set({ maxAge: 3600, tags });
}
---

When caching is not enabled, cache.set(), cache.tags, and cache.options will log a warning, and cache.invalidate() will throw an error. Use cache.enabled to check before calling these methods.

Call cache.set() with an options object to enable caching for the current response.

The following example caches a page for 2 minutes, serves stale content for 1 minute while revalidating, and tags the response for targeted invalidation:

src/pages/index.astro
---
export const prerender = false; // Not needed in 'server' mode
Astro.cache.set({
maxAge: 120,
swr: 60,
tags: ['home'],
});
---
<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 });
}

Call cache.set(false) to explicitly opt a request out of caching. This is useful when a matched route rule would otherwise cache the response:

src/pages/dashboard.astro
---
if (isPersonalized) {
Astro.cache.set(false);
}
---

Access the current accumulated cache options via cache.options:

src/pages/api/debug.ts
const { maxAge, swr, tags } = context.cache.options;

Multiple calls to cache.set() within a single request are merged:

  • Scalar values (maxAge, swr, etag): last-write-wins
  • lastModified: most recent date wins
  • tags: accumulate across all calls

Middleware, layouts, content loaders, and page code can each contribute cache directives independently.

Route caching integrates directly with live content collections. cache.set() accepts CacheHint and LiveDataEntry objects natively, allowing cache hints from loaders to be passed through without manually setting headers.

A live loader can return a cacheHint on individual entries or on the collection as a whole. These hints include tags (for targeted invalidation) and lastModified (for freshness). When passed to cache.set(), they merge with any other cache options already set on the page.

Pass the cacheHint returned by getLiveEntry() or getLiveCollection() directly to cache.set().

The following example passes the loader’s cache hint and adds a maxAge to control how long the response stays fresh:

src/pages/products/[id].astro
---
import { getLiveEntry } from 'astro:content';
const { entry, error, cacheHint } = await getLiveEntry('products', Astro.params.id);
if (error) {
return Astro.redirect('/404');
}
if (cacheHint) {
Astro.cache.set(cacheHint);
}
Astro.cache.set({ maxAge: 300 });
---
<h1>{entry.data.name}</h1>

A LiveDataEntry can also be passed directly. Astro extracts its cacheHint automatically:

src/pages/products/[id].astro
---
import { getLiveEntry } from 'astro:content';
const { entry, error } = await getLiveEntry('products', Astro.params.id);
if (error) {
return Astro.redirect('/404');
}
Astro.cache.set(entry);
Astro.cache.set({ maxAge: 300, swr: 60 });
---
<h1>{entry.data.name}</h1>

cache.invalidate() also accepts a LiveDataEntry, invalidating all cached responses tagged with that entry’s cache tags.

The following example invalidates the cached response for a specific product entry:

src/pages/api/revalidate.ts
import { getLiveEntry } from 'astro:content';
export async function POST(context) {
const { entry } = await getLiveEntry('products', 'featured');
if (entry) {
await context.cache.invalidate(entry);
}
return Response.json({ ok: true });
}

When fetching a full collection with getLiveCollection(), Astro merges cache hints from the collection response and all individual entries: tags are accumulated, and the most recent lastModified wins.

The following example passes the merged cache hint from a collection and sets a 10-minute freshness window:

src/pages/products/index.astro
---
import { getLiveCollection } from 'astro:content';
const { entries, error, cacheHint } = await getLiveCollection('products');
if (error) {
return new Response('Error loading products', { status: 500 });
}
if (cacheHint) {
Astro.cache.set(cacheHint);
}
Astro.cache.set({ maxAge: 600 });
---
<ul>
{entries.map((p) => <li>{p.data.name}</li>)}
</ul>

See the Content Loader Reference for more about implementing cache hints in your live loaders.

Purge cached entries by tag or path using cache.invalidate().

The following example creates an API route that invalidates by tag and by path:

src/pages/api/revalidate.ts
export async function POST(context) {
// Invalidate all entries tagged 'data'
await context.cache.invalidate({ tags: ['data'] });
// Invalidate a specific path
await context.cache.invalidate({ path: '/api/data' });
return Response.json({ purged: true });
}

Tag-based invalidation removes all cached entries whose tags include any of the provided tags. Path-based invalidation is exact-match only (no glob or wildcard patterns).

Type: Record<string, { maxAge?: number; swr?: number; tags?: string[]; }>
Default: undefined

أُضيفت في: astro@6.0.0 جديد

experimental.routeRules sets default cache options for routes declaratively in your config, without modifying route code. This is useful for applying caching to large groups of routes at once.

The following example caches all API routes with stale-while-revalidate, product pages with a 1-hour freshness window, and blog posts for 5 minutes:

astro.config.mjs
import { defineConfig, memoryCache } from 'astro/config';
export default defineConfig({
experimental: {
cache: {
provider: memoryCache(),
},
routeRules: {
'/api/*': { swr: 600 },
'/products/*': { maxAge: 3600, tags: ['products'] },
'/blog/[...slug]': { maxAge: 300, swr: 60 },
},
},
});

Route patterns support:

  • Static paths: /about, /api/health
  • Dynamic parameters: /products/[id], /blog/[slug]
  • Rest parameters: /docs/[...path]
  • Glob wildcards: /api/*

Patterns use the same matching and priority rules as Astro’s file-based routing, so more specific patterns take precedence.

Per-route cache.set() calls merge with config-level route rules. Route code can override or extend the defaults set in config.

Cache behavior is determined by the configured cache provider. Providers fall into two categories:

CDN providers translate cache directives into response headers (e.g. CDN-Cache-Control, Cache-Tag) and rely on a CDN or reverse proxy to handle caching. These internal headers are stripped before the response reaches the client.

A CDN provider implements setHeaders() to produce the appropriate headers.

Runtime providers implement onRequest() to intercept requests and cache responses in-process. They add an X-Astro-Cache response header for observability:

  • HIT: response served from cache
  • MISS: response rendered fresh and stored in cache
  • STALE: stale response served while revalidating in the background

Astro includes a built-in in-memory LRU runtime cache provider suitable for single-instance deployments. Import memoryCache from astro/config:

astro.config.mjs
import { defineConfig, memoryCache } from 'astro/config';
export default defineConfig({
experimental: {
cache: {
provider: memoryCache({ max: 500 }),
},
},
});

The memory provider automatically normalizes cache keys for better hit rates:

  • Query parameter sorting: Parameters are sorted alphabetically, so /page?b=2&a=1 and /page?a=1&b=2 resolve to the same cache entry.
  • Tracking parameter exclusion: Common analytics and tracking parameters (e.g. utm_source, fbclid, gclid) are excluded from the cache key by default. This prevents cache fragmentation from marketing links without affecting your page content.
  • Vary header support: When a response includes a Vary header, the memory provider uses the specified request header values to create separate cache entries for each variant. For example, a response with Vary: Accept-Language will cache different versions for different languages.

Type: { max?: number; query?: object }
Default: { max: 1000 }

Type: number
Default: 1000

Maximum number of entries to keep in cache. When the cache exceeds this limit, the least recently used entry is evicted.

Controls how query parameters are handled in cache keys.

Type: boolean
Default: true

Sort query parameters alphabetically so that parameter order does not affect the cache key. Set to false to disable sorting, and cache URLs with different query parameter order separately. This is useful when parameter order is significant.

Type: string[]
Default: ['utm_*', 'fbclid', 'gclid', 'gbraid', 'wbraid', 'dclid', 'msclkid', 'twclid', 'li_fat_id', 'mc_cid', 'mc_eid', '_ga', '_gl', '_hsenc', '_hsmi', '_ke', 'oly_anon_id', 'oly_enc_id', 'rb_clickid', 's_cid', 'vero_id', 'wickedid', 'yclid', '__s', 'ref']

Exclude query parameters whose names match these patterns from the cache key. Supports glob wildcards (e.g. "utm_*"). Cannot be used together with include.

By default, common tracking and analytics parameters are excluded. Set to [] to include all query parameters in the cache key:

astro.config.mjs
memoryCache({
query: { exclude: [] },
});

Type: string[]

Only include these query parameter names in the cache key. All other parameters are ignored, including the default tracking parameter exclusions. Cannot be used together with exclude.

The following example only uses page and sort parameters in the cache key, ignoring all others:

astro.config.mjs
memoryCache({
query: { include: ['page', 'sort'] },
});

A cache provider has two parts:

  1. The runtime module — A file that default-exports a CacheProviderFactory function. This module is bundled into your SSR output, so it must be runtime-agnostic: avoid Node.js built-in modules (e.g. node:fs, node:path) unless your target runtime supports them.

  2. The config helper — A function exported for users to call in astro.config.mjs. It returns a CacheProviderConfig object that tells Astro where to find the runtime module and what options to pass it. This is the same pattern used by memoryCache() from astro/config.

The following example shows a config helper that accepts typed options and points to a runtime module:

my-provider/config.ts
import type { CacheProviderConfig } from 'astro';
interface MyProviderOptions {
apiKey: string;
region?: string;
}
export function myCache(options: MyProviderOptions): CacheProviderConfig {
return {
entrypoint: 'my-provider/runtime', // resolved from the project root
config: options, // passed to the factory at runtime
};
}

The config helper is then called in the Astro config:

astro.config.mjs
import { defineConfig } from 'astro/config';
import { myCache } from 'my-provider/config';
export default defineConfig({
experimental: {
cache: {
provider: myCache({ apiKey: '...' }),
},
},
});

The runtime module default-exports a factory that receives the serialized config and returns a CacheProvider:

my-provider/runtime.ts
import type { CacheProviderFactory } from 'astro';
const factory: CacheProviderFactory = (config) => {
return {
name: 'my-cache-provider',
// CDN-style: translate cache options into response headers
setHeaders(options) {
const headers = new Headers();
if (options.maxAge !== undefined) {
let value = `max-age=${options.maxAge}`;
if (options.swr !== undefined) {
value += `, stale-while-revalidate=${options.swr}`;
}
headers.set('CDN-Cache-Control', value);
}
if (options.tags?.length) {
headers.set('Cache-Tag', options.tags.join(','));
}
return headers;
},
// Runtime-style: intercept requests (optional)
async onRequest(context, next) {
// Check cache, call next(), store response...
return next();
},
// Handle invalidation requests
async invalidate(options) {
// Purge by tags or path...
},
};
};
export default factory;

Describes a provider used for caching. This requires the name and invalidate() properties and accepts optional properties.

Type: string

A unique name for the provider, used in logs and for identification.

Type: (options: CacheOptions) => Headers

Translates cache options into response headers. Called after the response is rendered but before it is sent to the client. These headers are stripped from the final response.

Type: (context: { request: Request; url: URL; waitUntil?: (promise: Promise<unknown>) => void }, next: MiddlewareNext) => Promise<Response>

Intercepts requests to implement runtime caching. The context includes a waitUntil function (when available in the runtime) for background work such as stale-while-revalidate.

Type: (options: InvalidateOptions) => Promise<void>

Handles purge requests by tag or path.

Type: (config: Record<string, any> | undefined) => CacheProvider

The factory function type. Receives the provider’s serializable config object from the Astro config.

Type: boolean

Whether caching is active. Returns false when no cache provider is configured or in development mode. Returns true when a cache provider is configured and the app is running in production.

Type: (options: CacheOptions | false) => void

Sets cache options for the current request. Pass false to opt out of caching.

Type: number

Time in seconds the response is considered fresh.

Type: number

Stale-while-revalidate window in seconds. Stale content is served while a fresh response is generated in the background.

Type: string[]

Cache tags for targeted invalidation. Tags accumulate across multiple set() calls.

Type: Date

When multiple set() calls provide lastModified, the most recent date wins.

Type: string

Entity tag for conditional requests.

Type: Readonly<CacheOptions>

Read-only snapshot of the current accumulated cache options, including all merged maxAge, swr, etag, lastModified, and tags values.

Type: string[]

Read-only array of all accumulated cache tags.

Type: (options: InvalidateOptions) => Promise<void>

Purges cached entries. Requires a configured cache provider.

Type: string

Exact path to invalidate. No glob or wildcard support.

Type: string | string[]

Tag or tags to invalidate. All entries with matching tags are purged.

Type: { entrypoint: string | URL; config?: Record<string, any> }

The configuration object passed to experimental.cache.provider. Use a helper function (e.g. memoryCache()) for type-safe configuration.

Thrown when cache.invalidate() is called without a configured cache provider. Other cache methods (set(), tags, options) no-op when caching is not configured, logging a one-time warning on the first set() call.

Thrown at build time when the configured cache provider cannot be resolved. This typically means the package is not installed or the import path is incorrect.

In dev mode, the cache API is available so that route code does not need conditional checks, but no actual caching occurs. cache.enabled is false, and cache.set() and cache.invalidate() are no-ops.

For full details and to give feedback on this experimental API, see the Route Caching RFC.

ساهم المجتمع راعي