From 2c2d4ea971b55ace0afaac97818bbdf25c398e5a Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 19 Jun 2026 15:12:06 +0100 Subject: [PATCH 1/3] Add adapter CDN cache provider docs and provider-utils reference - Document the Netlify, Vercel, and Cloudflare CDN cache providers (manual opt-in; Cloudflare private beta) in the caching guide - Add an astro/cache/provider-utils reference section and the setHeaders(options, request) signature - Use file-based routing patterns ([...rest]) instead of unsupported globs --- src/content/docs/en/guides/caching.mdx | 93 ++++++++++++++++++- .../en/reference/cache-provider-reference.mdx | 65 ++++++++++++- 2 files changed, 153 insertions(+), 5 deletions(-) diff --git a/src/content/docs/en/guides/caching.mdx b/src/content/docs/en/guides/caching.mdx index 93bdb169a0c75..47b86af3924fc 100644 --- a/src/content/docs/en/guides/caching.mdx +++ b/src/content/docs/en/guides/caching.mdx @@ -32,6 +32,92 @@ export default defineConfig({ You can then use [`Astro.cache`](/en/reference/api-reference/#cache) in your `.astro` pages (or `context.cache` for API routes and middleware) to control caching per request. Cache defaults for groups of routes can also be defined declaratively in your config using [`routeRules`](#route-rules). +If you deploy to Netlify, Vercel, or Cloudflare, use the adapter's [CDN cache provider](#adapter-cache-providers) instead of the in-memory provider. + +## Adapter cache providers + +Astro's first-party adapters for Netlify, Vercel, and Cloudflare each provide a CDN cache provider that maps cache directives to the platform's native cache headers and invalidation API. Rather than storing responses in memory, these push your caching directives to the host's edge network, and cache hits are served straight from the CDN without invoking your server function. + +For now, you configure these providers manually, as shown below. In a future release, the Netlify and Vercel providers will be enabled automatically by their adapters. The Cloudflare provider requires a feature that is currently in private beta. + +Each provider automatically tags cached responses with the request path, so `cache.invalidate({ path })` works on platforms that only support tag-based purges. + +### Netlify + +

+ + +

+ +Import `cacheNetlify()` from `@astrojs/netlify/cache` and set it as your cache provider: + +```js title="astro.config.mjs" ins={3, 7-9} +import { defineConfig } from 'astro/config'; +import netlify from '@astrojs/netlify'; +import { cacheNetlify } from '@astrojs/netlify/cache'; + +export default defineConfig({ + adapter: netlify(), + cache: { + provider: cacheNetlify(), + }, +}); +``` + +The provider sets `Netlify-CDN-Cache-Control` and `Netlify-Cache-Tag` headers. Cached responses use Netlify's [durable cache](https://docs.netlify.com/platform/caching/#durable-directive) so they are shared across all edge nodes, reducing function invocations. Both tag-based and path-based invalidation are supported. + +### Vercel + +

+ + +

+ +Import `cacheVercel()` from `@astrojs/vercel/cache` and set it as your cache provider: + +```js title="astro.config.mjs" ins={3, 7-9} +import { defineConfig } from 'astro/config'; +import vercel from '@astrojs/vercel'; +import { cacheVercel } from '@astrojs/vercel/cache'; + +export default defineConfig({ + adapter: vercel(), + cache: { + provider: cacheVercel(), + }, +}); +``` + +The provider sets `Vercel-CDN-Cache-Control` and `Vercel-Cache-Tag` headers. Both tag-based and path-based invalidation are supported. Tag invalidation is a soft invalidation: cached responses are marked as stale and revalidated in the background using stale-while-revalidate. + +### Cloudflare + +

+ + +

+ +:::caution[Private beta] +The Cloudflare provider requires the Cloudflare Workers Cache feature, which is currently in private beta. Without beta access it does not work and should not be used: Cloudflare Workers run in front of the cache, so cached responses are never served, and calling [`cache.invalidate()`](/en/reference/api-reference/#cacheinvalidate) throws an error. Only configure this provider if your account has access to the beta. +::: + +Import `cacheCloudflare()` from `@astrojs/cloudflare/cache` and set it as your cache provider: + +```js title="astro.config.mjs" ins={3, 7-9} +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; +import { cacheCloudflare } from '@astrojs/cloudflare/cache'; + +export default defineConfig({ + adapter: cloudflare(), + cache: { + provider: cacheCloudflare(), + }, +}); +``` + +The provider sets `Cloudflare-CDN-Cache-Control` and `Cache-Tag` headers, which are read by Cloudflare's built-in Worker cache. The adapter enables Worker caching automatically when a Cloudflare cache provider is configured. Both tag-based and path-based invalidation are supported. + ## Interacting with the cache The [`cache` object](/en/reference/api-reference/#cache) provides methods for setting cache options, invalidating entries, and checking the current cache state. This object is available in your `.astro` pages as `Astro.cache`, and in API routes and middleware as `context.cache`. @@ -153,8 +239,8 @@ export default defineConfig({ provider: memoryCache(), }, routeRules: { - '/api/*': { swr: 600 }, - '/products/*': { maxAge: 3600, tags: ['products'] }, + '/api/[...path]': { swr: 600 }, + '/products/[...slug]': { maxAge: 3600, tags: ['products'] }, '/blog/[...slug]': { maxAge: 300, swr: 60 }, }, }); @@ -165,8 +251,7 @@ The following route patterns are supported: - **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](/en/guides/routing/#route-priority-order), so more specific patterns take precedence. +Patterns use the same syntax, matching, and priority rules as Astro's [file-based routing](/en/guides/routing/#route-priority-order), so more specific patterns take precedence. Glob wildcards such as `*` are not supported; use a `[...rest]` parameter to match a group of routes (for example, `/api/[...path]` to match everything under `/api`). Per-route [`cache.set()`](/en/reference/api-reference/#cacheset) calls merge with config-level route rules. Route code can override or extend the defaults set in config. For example, a route rule might set a default `maxAge` for all product pages, but individual pages can call `cache.set()` to customize or disable caching as needed. diff --git a/src/content/docs/en/reference/cache-provider-reference.mdx b/src/content/docs/en/reference/cache-provider-reference.mdx index aac271709ece2..3bf299f507ae2 100644 --- a/src/content/docs/en/reference/cache-provider-reference.mdx +++ b/src/content/docs/en/reference/cache-provider-reference.mdx @@ -218,6 +218,67 @@ const factory: CacheProviderFactory = (config) => { export default factory; ``` +When writing a CDN provider, Astro provides [shared utilities](#cache-provider-utilities) for building cache-control headers and path-based invalidation tags. + +## Cache provider utilities + +Astro exports shared helpers for CDN provider authors from `astro/cache/provider-utils`. These are the same helpers used by the first-party Netlify, Vercel, and Cloudflare providers. + +```ts +import { + buildCacheControlDirectives, + pathTag, + setConditionalHeaders, + collectInvalidationTags, + normalizeTags, +} from 'astro/cache/provider-utils'; +``` + +### `buildCacheControlDirectives()` + +

+ +**Type:** (options: CacheOptions, extraDirectives?: string[]) => string | undefined +

+ +Builds a cache-control directive string (e.g. `"public, max-age=300, stale-while-revalidate=60"`) from `CacheOptions`, without a header name so each provider can apply its own (such as `Netlify-CDN-Cache-Control` or `Cloudflare-CDN-Cache-Control`). Pass `extraDirectives` to prepend platform directives such as `public` or `durable`. Returns `undefined` when there are no caching directives. + +### `pathTag()` + +

+ +**Type:** `(path: string) => string` +

+ +Generates a cache tag for a given path (`astro-path:{path}`). Used to support `invalidate({ path })` on platforms that only offer tag-based purging. + +### `setConditionalHeaders()` + +

+ +**Type:** (headers: Headers, options: CacheOptions) => void +

+ +Sets `Last-Modified` and `ETag` headers on a `Headers` object from the corresponding `CacheOptions` values. + +### `collectInvalidationTags()` + +

+ +**Type:** (options: InvalidateOptions) => string[] +

+ +Collects all tags needed to invalidate the given options, including the path tag when `options.path` is set. Used by providers that implement path invalidation via tags rather than native path purge. + +### `normalizeTags()` + +

+ +**Type:** `(tags: string | string[] | undefined) => string[]` +

+ +Normalizes a tag value to a flat string array. + ## Cache provider types reference The following types can be imported from the `astro` module: @@ -299,11 +360,13 @@ A unique name for the provider, used in logs and for identification. #### `CacheProvider.setHeaders()`

-**Type:** (options: CacheOptions) => Headers +**Type:** (options: CacheOptions, request: Request) => Headers

Translates cache options into response headers. This is called after the response is rendered but before it is sent to the client. These headers are stripped from the final response. +The incoming `request` is passed as a second argument so a provider can read the URL or request headers. This can be useful to auto-tag responses with their pathname for path-based invalidation. + #### `CacheProvider.onRequest()`

From 07b5e1ada61d5735a1692b3fad4135f10320e102 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 19 Jun 2026 15:34:03 +0100 Subject: [PATCH 2/3] Wording --- src/content/docs/en/guides/caching.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/content/docs/en/guides/caching.mdx b/src/content/docs/en/guides/caching.mdx index 47b86af3924fc..cfee3bbad7fba 100644 --- a/src/content/docs/en/guides/caching.mdx +++ b/src/content/docs/en/guides/caching.mdx @@ -32,13 +32,13 @@ export default defineConfig({ You can then use [`Astro.cache`](/en/reference/api-reference/#cache) in your `.astro` pages (or `context.cache` for API routes and middleware) to control caching per request. Cache defaults for groups of routes can also be defined declaratively in your config using [`routeRules`](#route-rules). -If you deploy to Netlify, Vercel, or Cloudflare, use the adapter's [CDN cache provider](#adapter-cache-providers) instead of the in-memory provider. +If you deploy to Netlify, Vercel, or Cloudflare, you can use their respective adapters' experimental [CDN cache provider](#adapter-cache-providers) instead of the in-memory provider. ## Adapter cache providers -Astro's first-party adapters for Netlify, Vercel, and Cloudflare each provide a CDN cache provider that maps cache directives to the platform's native cache headers and invalidation API. Rather than storing responses in memory, these push your caching directives to the host's edge network, and cache hits are served straight from the CDN without invoking your server function. +Astro's first-party adapters for Netlify, Vercel, and Cloudflare each provide an experimental CDN cache provider that maps cache directives to the platform's native cache headers and invalidation API. Rather than storing responses in memory, these push your caching directives to the host's edge network, and cache hits are served straight from the CDN without invoking your server function. -For now, you configure these providers manually, as shown below. In a future release, the Netlify and Vercel providers will be enabled automatically by their adapters. The Cloudflare provider requires a feature that is currently in private beta. +During the experimental phase these providers need to be manually enabled, as shown below. In a future release, the Netlify and Vercel providers will be enabled automatically by their adapters. The Cloudflare provider requires a feature that is currently in private beta. Each provider automatically tags cached responses with the request path, so `cache.invalidate({ path })` works on platforms that only support tag-based purges. @@ -98,7 +98,7 @@ The provider sets `Vercel-CDN-Cache-Control` and `Vercel-Cache-Tag` headers. Bot

:::caution[Private beta] -The Cloudflare provider requires the Cloudflare Workers Cache feature, which is currently in private beta. Without beta access it does not work and should not be used: Cloudflare Workers run in front of the cache, so cached responses are never served, and calling [`cache.invalidate()`](/en/reference/api-reference/#cacheinvalidate) throws an error. Only configure this provider if your account has access to the beta. +The Cloudflare provider requires the Cloudflare Workers Cache feature, which is currently in private beta. Without beta access the headers are ignored, and calling `cache.invalidate()` will throw an error. ::: Import `cacheCloudflare()` from `@astrojs/cloudflare/cache` and set it as your cache provider: @@ -116,7 +116,7 @@ export default defineConfig({ }); ``` -The provider sets `Cloudflare-CDN-Cache-Control` and `Cache-Tag` headers, which are read by Cloudflare's built-in Worker cache. The adapter enables Worker caching automatically when a Cloudflare cache provider is configured. Both tag-based and path-based invalidation are supported. +The provider sets `Cloudflare-CDN-Cache-Control` and `Cache-Tag` headers. The adapter enables Worker caching automatically when a Cloudflare cache provider is configured. Both tag-based and path-based invalidation are supported. ## Interacting with the cache From 8736a94ee46e789bb860ec08c96a7c4ef6cacd5f Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 19 Jun 2026 16:25:59 +0100 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Armand Philippot --- src/content/docs/en/guides/caching.mdx | 2 +- src/content/docs/en/reference/cache-provider-reference.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/en/guides/caching.mdx b/src/content/docs/en/guides/caching.mdx index cfee3bbad7fba..32174d6abfc09 100644 --- a/src/content/docs/en/guides/caching.mdx +++ b/src/content/docs/en/guides/caching.mdx @@ -40,7 +40,7 @@ Astro's first-party adapters for Netlify, Vercel, and Cloudflare each provide an During the experimental phase these providers need to be manually enabled, as shown below. In a future release, the Netlify and Vercel providers will be enabled automatically by their adapters. The Cloudflare provider requires a feature that is currently in private beta. -Each provider automatically tags cached responses with the request path, so `cache.invalidate({ path })` works on platforms that only support tag-based purges. +Each provider automatically tags cached responses with the request path, so [`cache.invalidate({ path })`](/en/reference/api-reference/#cacheinvalidate) works on platforms that only support tag-based purges. ### Netlify diff --git a/src/content/docs/en/reference/cache-provider-reference.mdx b/src/content/docs/en/reference/cache-provider-reference.mdx index 3bf299f507ae2..09199d020ebaf 100644 --- a/src/content/docs/en/reference/cache-provider-reference.mdx +++ b/src/content/docs/en/reference/cache-provider-reference.mdx @@ -250,7 +250,7 @@ Builds a cache-control directive string (e.g. `"public, max-age=300, stale-while **Type:** `(path: string) => string`

-Generates a cache tag for a given path (`astro-path:{path}`). Used to support `invalidate({ path })` on platforms that only offer tag-based purging. +Generates a cache tag for a given path (`astro-path:{path}`). Used to support [`invalidate({ path })`](/en/reference/api-reference/#cacheinvalidate) on platforms that only offer tag-based purging. ### `setConditionalHeaders()`