Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 89 additions & 4 deletions src/content/docs/en/guides/caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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 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.

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.
Comment thread
ascorbic marked this conversation as resolved.
Resolved

### Netlify

<p>

<Since pkg="@astrojs/netlify" v="8.0.0" />
</p>

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

<p>

<Since pkg="@astrojs/vercel" v="11.0.0" />
</p>

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

<p>

<Since pkg="@astrojs/cloudflare" v="14.0.0" />
</p>

:::caution[Private beta]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know when this will leave the private beta? Is it simply an update to the Cloudflare package, or does it require a change to Astro's code? What I mean is, if it's just a package update (ie. renovate...), we might forget to update the documentation. So, I just want to know if this is something we need to monitor or not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a Cloudflare platform thing, out of our hands. I'm monitoring it and will update the docs.

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:

```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. 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`.
Expand Down Expand Up @@ -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 },
},
});
Expand All @@ -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.
65 changes: 64 additions & 1 deletion src/content/docs/en/reference/cache-provider-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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()`

<p>

**Type:** <code>(options: <a href="#cacheoptions">CacheOptions</a>, extraDirectives?: string[]) => string | undefined</code>
</p>

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()`

<p>

**Type:** `(path: string) => string`
</p>

Generates a cache tag for a given path (`astro-path:{path}`). Used to support `invalidate({ path })` on platforms that only offer tag-based purging.
Comment thread
ascorbic marked this conversation as resolved.
Resolved

### `setConditionalHeaders()`

<p>

**Type:** <code>(headers: Headers, options: <a href="#cacheoptions">CacheOptions</a>) => void</code>
</p>

Sets `Last-Modified` and `ETag` headers on a `Headers` object from the corresponding `CacheOptions` values.

### `collectInvalidationTags()`

<p>

**Type:** <code>(options: <a href="#invalidateoptions">InvalidateOptions</a>) => string[]</code>
</p>

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()`

<p>

**Type:** `(tags: string | string[] | undefined) => string[]`
</p>

Normalizes a tag value to a flat string array.

## Cache provider types reference

The following types can be imported from the `astro` module:
Expand Down Expand Up @@ -299,11 +360,13 @@ A unique name for the provider, used in logs and for identification.
#### `CacheProvider.setHeaders()`
<p>

**Type:** <code>(options: <a href="#cacheoptions">CacheOptions</a>) => Headers</code>
**Type:** <code>(options: <a href="#cacheoptions">CacheOptions</a>, request: Request) => Headers</code>
</p>

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()`

<p>
Expand Down
Loading