Skip to content

Route Caching#1245

Open
ascorbic wants to merge 12 commits intomainfrom
feat/route-caching
Open

Route Caching#1245
ascorbic wants to merge 12 commits intomainfrom
feat/route-caching

Conversation

@ascorbic
Copy link
Contributor

@ascorbic ascorbic commented Oct 15, 2025

Summary

A platform-agnostic route caching API for Astro SSR pages that enables declarative cache control using web standards.

Examples

Basic route caching

---
// src/pages/products/[id].astro
import { getEntry } from 'astro:content';
const product = await getEntry('products', Astro.params.id);
Astro.cache.set({
  lastModified: product.updatedAt,
  maxAge: 300,  // Cache for 5 minutes
  swr: 3600,    // Stale-while-revalidate for 1 hour
  tags: ['products', `product:${product.id}`]
});
---
<h1>{product.data.name}</h1>
<p>{product.data.description}</p>

Automatic dependency tracking

// src/pages/api/revalidate.ts
import { getLiveEntry } from "astro:content";

export const POST: APIRoute = async ({ cache, request }) => {
  const { id } = await request.json();
  const { entry } = await getLiveEntry("products", id);

  // Invalidate all pages that depend on this entry
  cache.invalidate(entry);

  return Response.json({ ok: true });
};

Links

@ascorbic ascorbic mentioned this pull request Oct 15, 2025
Added cache configuration for routes in astro.config.ts and updated cache invalidation tag in webhook API.
@wildfiremedia
Copy link

I’d like to see a summary of the total cache along with a list of tags, so we can get an idea of what’s being cached or invalidated and catch any tags that might have been missed which allows us to easily debug.

@florian-lefebvre
Copy link
Member

I'm not sure that's something for Astro to provide, feels like maybe it should be achievable through the driver platform (eg. cloudflare)?

@wildfiremedia
Copy link

wildfiremedia commented Oct 21, 2025

Exclusively for self-hosted servers, these metrics (memory, SWR settings, etc.) are provided in JSON format. I think they could be useful for storefronts that need to cache product listings.

Other ideas, can it cache payload in gzipped or brotli? Thinking by 50% or more is worth saving and reduce latency.

@stipsan
Copy link

stipsan commented Nov 14, 2025

From the perspective of Sanity I think we'd like to automatically define cache tags since our backend provides them, regardless of how customers define their GROQ queries and how the data is fetched, it's fairly dynamic.
But we wouldn't want to define maxAge or swr automatically.

For us if we can define a live loader that could call Astro.cacheTag(...tags: string[]) and have it hoist up/merge with the userland Astro.cache() call that would be ideal.
Since we allow resolving references, which is similar to a db join, even getting a single document entry might have linked data that have multiple cache tag.

A blog post could be resolving an author reference. If the author reference is edited it should invalidate all post entries that use it.
For that to work without too much wiring we'd need to define all the cache tags that can be used for invalidation on both the get entry and on the collection levels.

Since customers often fetch data from other sources than ours we can't make any assumptions on their maxAge and swr ideal settings.

For next.js we're able to do this by calling cacheTag, and changing the revalidate setting on cacheLife. Userland can override these settings by having the callsite use shorter revaldiate times, as well as different settings for stale and expire. I'd like for Astro users to have the same, simple, API surface.

@ascorbic
Copy link
Contributor Author

ascorbic commented Feb 11, 2026

I've been going through the RFC and made some changes to clarify things, tidy things up etc.

Astro.cache as an object with methods

Changed from Astro.cache() (callable) to Astro.cache.set(). This is more consistent with other Astro global (Astro.cookies.set(), Astro.session.set()) and allows us to add invalidate without an unusual callable-with-methods pattern. The full AstroCache interface:

interface AstroCache {
  set(options: CacheOptions | CacheHint | LiveDataEntry | false): void;
  readonly tags: string[];
  invalidate(options: InvalidateOptions | LiveDataEntry): Promise<void>;
}

Available as Astro.cache in pages and context.cache in API routes/middleware.

Merge semantics for multiple cache.set() calls

Calling cache.set() multiple times merges rather than replaces:

  • maxAge, swr, etag: last-write-wins
  • lastModified: most recent date wins
  • tags: accumulated (union, deduplicated)
  • false: overrides everything, disables caching

This matches the existing semantics for live collecitons, and makes it natural to compose cache hints from multiple data sources (e.g. a page that depends on both a product and an author entry). @stipsan's Sanity use case would usually do this via the live collections API, which allows tags to be returned as part of the loader response.

cache.set(false) for opting out

If a route matches a config-level cache rule but shouldn't be cached, Astro.cache.set(false) explicitly disables caching.

Unified invalidation on Astro.cache

Per @florian-lefebvre's feedback, invalidation now lives on the same object: Astro.cache.invalidate() in pages, context.cache.invalidate() in API routes/middleware.

InvalidateOptions simplified

Removed the tag/tags duality. Now just tags: string | string[].

API routes can set cache, not just invalidate

Added examples showing cache.set() in API routes and middleware, not just cache.invalidate().

Config route precedence clarified

Explicitly documented: no merging between config patterns, most specific wins, each pattern must be fully specified. Priority order:

  1. cache.set(false) in route (disables)
  2. cache.set() in route or middleware
  3. Most specific config pattern
  4. Less specific config patterns
  5. No caching (default)

No caching in dev

Explicitly documented that caching is not active during development. The API is available but calls are no-ops.

Header stripping clarified

For header-based providers, the CDN strips CDN-Cache-Control per RFC 9213. For runtime providers, the framework strips CDN-Cache-Control and Cache-Tag from the outgoing response.

export default defineConfig({
adapter: node(),
cache: {
routes: {
Copy link
Member

Choose a reason for hiding this comment

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

This makes me think about https://nitro.build/config#routerules in nitro. So I'm wondering: is there any benefit to have this under cache? Instead that could be on the top level and we could also set prerender: boolean there.

So to recap, this looks good to me as is but maybe there's a way to future proof it and enable more cool things

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, yeah. Good idea. routeRules would be a better name in that scneario too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if we just directly copy the Nitro pattern, with "swr: true|number is shortcut for cache: { swr: true, maxAge: number }"

Copy link
Member

Choose a reason for hiding this comment

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

A recurring problem with supporting different shapes it doesn't work very nicely with updateConfig(). Otherwise no strong opinion


Caching is essential for performant server-rendered applications, but current solutions have significant limitations:

**Platform-specific implementations**: Most caching solutions (like ISR) are tightly coupled to specific hosting platforms. ISR was created for Next.js on Vercel and while other platforms have implemented versions of it, they're often limited or second-class implementations. This creates vendor lock-in and inconsistent behavior across deployments.
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so, or leave it deprecated and make it conflice with the new caching

- **Distributed caching for Node.js**: Node adapter uses in-memory cache, unsuitable for multi-instance deployments without external cache
- **Static/prerendered page caching**: This is for on-demand SSR routes only; static and prerendered routes are already cached by default and don't need route-level cache control
- **Partial page caching**: This focuses on full-page route caching, not fragment or component-level caching
- **Browser caching**: This focuses on CDN and server-side caching, not browser caching. Only `Last-Modified` and `ETag` headers are sent to browsers for conditional requests
Copy link
Member

@florian-lefebvre florian-lefebvre Feb 12, 2026

Choose a reason for hiding this comment

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

Can users access the current state of the cache (eg. after several cache.set() calls) so they can use this data to add browser caching headers? So not only Astro.cache.tags


```ts
interface CacheProvider {
name: string;
Copy link
Member

Choose a reason for hiding this comment

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

It's probably more useful to have the name in the cache.provider than the runtime implementation, so it can be used in logging etc

@ascorbic
Copy link
Contributor Author

@florian-lefebvre I have done an update that among other things moves the rules from cache.routes to routeRules

Memory cache moved to core. Config function is now memoryCache()
from astro/config instead of cacheMemory() from @astrojs/node/cache.
dadezzz added a commit to dadezzz/ice-notes that referenced this pull request Mar 14, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`5.18.1` → `6.0.2`](https://renovatebot.com/diffs/npm/astro/5.18.1/6.0.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/6.0.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/5.18.1/6.0.2?slim=true) |

---

### Release Notes

<details>
<summary>withastro/astro (astro)</summary>

### [`v6.0.2`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#602)

[Compare Source](https://github.com/withastro/astro/compare/astro@6.0.1...astro@6.0.2)

##### Patch Changes

- [#&#8203;15832](https://github.com/withastro/astro/pull/15832) [`95e12a2`](https://github.com/withastro/astro/commit/95e12a250ece206f55f8c0c07c9c05489f3df93f) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Fixes `return;` syntax not working in the frontmatter correctly in certain contexts

### [`v6.0.1`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#601)

[Compare Source](https://github.com/withastro/astro/compare/astro@6.0.0...astro@6.0.1)

##### Patch Changes

- [#&#8203;15827](https://github.com/withastro/astro/pull/15827) [`a4c0d0b`](https://github.com/withastro/astro/commit/a4c0d0b4df540b23fa85bf926f9cc97470737fa1) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes `astro add` so the tsconfig preview shows the actual pending changes before confirmation

### [`v6.0.0`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#600)

[Compare Source](https://github.com/withastro/astro/compare/astro@5.18.1...astro@6.0.0)

##### Major Changes

- [#&#8203;14446](https://github.com/withastro/astro/pull/14446) [`ece667a`](https://github.com/withastro/astro/commit/ece667a737a96c8bfea2702de7207bed0842b37c) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes `entryPoints` on `astro:build:ssr` hook (Integration API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-entrypoints-on-astrobuildssr-hook-integration-api))

- [#&#8203;15535](https://github.com/withastro/astro/pull/15535) [`dfe2e22`](https://github.com/withastro/astro/commit/dfe2e22042f92172442ab32777b3cce90685b76a) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates `loadManifest()` and `loadApp()` from `astro/app/node` (Adapter API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-loadmanifest-and-loadapp-from-astroappnode-adapter-api))

- [#&#8203;15006](https://github.com/withastro/astro/pull/15006) [`f361730`](https://github.com/withastro/astro/commit/f361730bc820c01a2ec3e508ac940be8077d8c04) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes session `test` driver - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-session-test-driver))

- [#&#8203;15461](https://github.com/withastro/astro/pull/15461) [`9f21b24`](https://github.com/withastro/astro/commit/9f21b243d21478cdc5fb0193e05adad8e753839f) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - **BREAKING CHANGE to the v6 beta Adapter API only**: renames `entryType` to `entrypointResolution` and updates possible values

  Astro 6 introduced a way to let adapters have more control over the entrypoint by passing `entryType: 'self'` to `setAdapter()`. However during beta development, the name was unclear and confusing.

  `entryType` is now renamed to `entrypointResolution` and its possible values are updated:

  - `legacy-dynamic` becomes `explicit`.
  - `self` becomes `auto`.

  If you are building an adapter with v6 beta and specifying `entryType`, update it:

  ```diff
  setAdapter({
      // ...
  -    entryType: 'legacy-dynamic'
  +    entrypointResolution: 'explicit'
  })

  setAdapter({
      // ...
  -    entryType: 'self'
  +    entrypointResolution: 'auto'
  })
  ```

- [#&#8203;14426](https://github.com/withastro/astro/pull/14426) [`861b9cc`](https://github.com/withastro/astro/commit/861b9cc770a05d9fcfcb2f1f442a3ba41e94b510) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the deprecated `emitESMImage()` function - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-emitesmimage))

- [#&#8203;15006](https://github.com/withastro/astro/pull/15006) [`f361730`](https://github.com/withastro/astro/commit/f361730bc820c01a2ec3e508ac940be8077d8c04) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates session driver string signature - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-session-driver-string-signature))

- [#&#8203;15180](https://github.com/withastro/astro/pull/15180) [`8780ff2`](https://github.com/withastro/astro/commit/8780ff2926d59ed196c70032d2ae274b8415655c) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Adds support for converting SVGs to raster images (PNGs, WebP, etc) to the default Sharp image service - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-svg-rasterization))

- [#&#8203;14446](https://github.com/withastro/astro/pull/14446) [`ece667a`](https://github.com/withastro/astro/commit/ece667a737a96c8bfea2702de7207bed0842b37c) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes `routes` on `astro:build:done` hook (Integration API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-routes-on-astrobuilddone-hook-integration-api))

- [#&#8203;15424](https://github.com/withastro/astro/pull/15424) [`33d6146`](https://github.com/withastro/astro/commit/33d6146e4872bb1e3feef0a6c0ea8e62f49f4c7e) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Throws an error when `getImage()` from `astro:assets` is called on the client - ([v6 upgrade guidance](https://v6.docs.astro.build/en/guides/upgrade-to/v6/#changed-getimage-throws-when-called-on-the-client))

- [#&#8203;14462](https://github.com/withastro/astro/pull/14462) [`9fdfd4c`](https://github.com/withastro/astro/commit/9fdfd4c620313827e65664632a9c9cb435ad07ca) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the old `app.render()` signature (Adapter API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-old-apprender-signature-adapter-api))

- [#&#8203;14956](https://github.com/withastro/astro/pull/14956) [`0ff51df`](https://github.com/withastro/astro/commit/0ff51dfa3c6c615af54228e159f324034472b1a2) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Astro v6.0 upgrades to Zod v4 for schema validation - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#zod-4))

- [#&#8203;14759](https://github.com/withastro/astro/pull/14759) [`d7889f7`](https://github.com/withastro/astro/commit/d7889f768a4d27e8c4ad3a0022099d19145a7d58) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates how schema types are inferred for content loaders with schemas (Loader API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-schema-types-are-inferred-instead-of-generated-content-loader-api))

- [#&#8203;15192](https://github.com/withastro/astro/pull/15192) [`ada2808`](https://github.com/withastro/astro/commit/ada2808a23fc70ea1f1663f3e1b69c6b735251e5) Thanks [@&#8203;gameroman](https://github.com/gameroman)! - Removes support for CommonJS config files - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-support-for-commonjs-config-files))

- [#&#8203;14462](https://github.com/withastro/astro/pull/14462) [`9fdfd4c`](https://github.com/withastro/astro/commit/9fdfd4c620313827e65664632a9c9cb435ad07ca) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes `prefetch()` `with` option - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-prefetch-with-option))

- [#&#8203;14306](https://github.com/withastro/astro/pull/14306) [`141c4a2`](https://github.com/withastro/astro/commit/141c4a26419fe5bb4341953ea5a0a861d9b398c0) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Removes support for routes with percent-encoded percent signs (e.g. `%25`) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-percent-encoding-in-routes))

- [#&#8203;14432](https://github.com/withastro/astro/pull/14432) [`b1d87ec`](https://github.com/withastro/astro/commit/b1d87ec3bc2fbe214437990e871df93909d18f62) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates `Astro` in `getStaticPaths()` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-astro-in-getstaticpaths))

- [#&#8203;14759](https://github.com/withastro/astro/pull/14759) [`d7889f7`](https://github.com/withastro/astro/commit/d7889f768a4d27e8c4ad3a0022099d19145a7d58) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the option to define dynamic schemas in content loaders as functions and adds a new equivalent `createSchema()` property (Loader API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-schema-function-signature-content-loader-api))

- [#&#8203;14457](https://github.com/withastro/astro/pull/14457) [`049da87`](https://github.com/withastro/astro/commit/049da87cb7ce1828f3025062ce079dbf132f5b86) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates trailing slash behavior of endpoint URLs - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-endpoints-with-a-file-extension-cannot-be-accessed-with-a-trailing-slash))

- [#&#8203;14494](https://github.com/withastro/astro/pull/14494) [`727b0a2`](https://github.com/withastro/astro/commit/727b0a205eb765f1c36f13a73dfc69e17e44df8f) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates Markdown heading ID generation - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-markdown-heading-id-generation))

- [#&#8203;14461](https://github.com/withastro/astro/pull/14461) [`55a1a91`](https://github.com/withastro/astro/commit/55a1a911aa4e0d38191f8cb9464ffd58f3eb7608) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates `import.meta.env.ASSETS_PREFIX` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-importmetaenvassets_prefix))

- [#&#8203;14586](https://github.com/withastro/astro/pull/14586) [`669ca5b`](https://github.com/withastro/astro/commit/669ca5b0199d9933f54c76448de9ec5a9f13c430) Thanks [@&#8203;ocavue](https://github.com/ocavue)! - Changes the values allowed in `params` returned by `getStaticPaths()` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-getstaticpaths-cannot-return-params-of-type-number))

- [#&#8203;15668](https://github.com/withastro/astro/pull/15668) [`1118ac4`](https://github.com/withastro/astro/commit/1118ac4f299341e15061e8a4e6e8423071c4d41c) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Changes TypeScript configuration - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-typescript-configuration))

- [#&#8203;14421](https://github.com/withastro/astro/pull/14421) [`df6d2d7`](https://github.com/withastro/astro/commit/df6d2d7bbcaf6b6a327a37a6437d4adade6e2485) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the previously deprecated `Astro.glob()` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-astroglob))

- [#&#8203;15049](https://github.com/withastro/astro/pull/15049) [`beddfeb`](https://github.com/withastro/astro/commit/beddfeb31e807cb472a43fa56c69f85dbadc9604) Thanks [@&#8203;Ntale3](https://github.com/Ntale3)! - Removes the ability to render Astro components in Vitest client environments - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-astro-components-cannot-be-rendered-in-vitest-client-environments-container-api))

- [#&#8203;15461](https://github.com/withastro/astro/pull/15461) [`9f21b24`](https://github.com/withastro/astro/commit/9f21b243d21478cdc5fb0193e05adad8e753839f) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates `createExports()` and `start()` (Adapter API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-createexports-and-start-adapter-api))

- [#&#8203;15535](https://github.com/withastro/astro/pull/15535) [`dfe2e22`](https://github.com/withastro/astro/commit/dfe2e22042f92172442ab32777b3cce90685b76a) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates `NodeApp` from `astro/app/node` (Adapter API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-nodeapp-from-astroappnode-adapter-api))

- [#&#8203;14462](https://github.com/withastro/astro/pull/14462) [`9fdfd4c`](https://github.com/withastro/astro/commit/9fdfd4c620313827e65664632a9c9cb435ad07ca) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the `handleForms` prop for the `<ClientRouter />` component - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-handleforms-prop-for-the-clientrouter--component))

- [#&#8203;14427](https://github.com/withastro/astro/pull/14427) [`e131261`](https://github.com/withastro/astro/commit/e1312615b39c59ebc05d5bb905ee0960b50ad3cf) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Increases minimum Node.js version to 22.12.0 - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#node-22))

- [#&#8203;15332](https://github.com/withastro/astro/pull/15332) [`7c55f80`](https://github.com/withastro/astro/commit/7c55f80fa1fd91f8f71ad60437f81e6c7f98f69d) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds frontmatter parsing support to `renderMarkdown` in content loaders. When markdown content includes frontmatter, it is now extracted and available in `metadata.frontmatter`, and excluded from the HTML output. This makes `renderMarkdown` behave consistently with the `glob` loader.

  ```js
  const loader = {
    name: 'my-loader',
    load: async ({ store, renderMarkdown }) => {
      const content = `---
  title: My Post
  ---

  # Hello World
  `;
      const rendered = await renderMarkdown(content);
      // rendered.metadata.frontmatter is now { title: 'My Post' }
      // rendered.html contains only the content, not the frontmatter
    },
  };
  ```

- [#&#8203;14400](https://github.com/withastro/astro/pull/14400) [`c69c7de`](https://github.com/withastro/astro/commit/c69c7de1ffeff29f919d97c262f245927556f875) Thanks [@&#8203;ellielok](https://github.com/ellielok)! - Removes the deprecated `<ViewTransitions />` component - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-viewtransitions--component))

- [#&#8203;14306](https://github.com/withastro/astro/pull/14306) [`141c4a2`](https://github.com/withastro/astro/commit/141c4a26419fe5bb4341953ea5a0a861d9b398c0) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Removes `RouteData.generate` from the Integration API - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-routedatagenerate-adapter-api))

- [#&#8203;14406](https://github.com/withastro/astro/pull/14406) [`4f11510`](https://github.com/withastro/astro/commit/4f11510c9ed932f5cb6d1075b1172909dd5db23e) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Changes the default routing configuration value of `i18n.routing.redirectToDefaultLocale` from `true` to `false` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-i18nroutingredirecttodefaultlocale-default-value))

- [#&#8203;14989](https://github.com/withastro/astro/pull/14989) [`73e8232`](https://github.com/withastro/astro/commit/73e823201cc5bdd6ccab14c07ff0dca5117436ad) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates exposed `astro:transitions` internals - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-exposed-astrotransitions-internals))

- [#&#8203;15726](https://github.com/withastro/astro/pull/15726) [`6f19ecc`](https://github.com/withastro/astro/commit/6f19ecc35adfb2ddaabbba2269630f95c13f5a57) Thanks [@&#8203;ocavue](https://github.com/ocavue)! - Updates dependency `shiki` to v4

  Check [Shiki's upgrade guide](https://shiki.style/blog/v4).

- [#&#8203;14758](https://github.com/withastro/astro/pull/14758) [`010f773`](https://github.com/withastro/astro/commit/010f7731becbaaba347da21ef07b8a410e31442a) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the `setManifestData` method from `App` and `NodeApp` (Adapter API) - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-appsetmanifestdata-adapter-api))

- [#&#8203;14477](https://github.com/withastro/astro/pull/14477) [`25fe093`](https://github.com/withastro/astro/commit/25fe09396dbcda2e1008c01a982f4eb2d1f33ae6) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes `rewrite()` from Actions context - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-rewrite-from-actions-context))

- [#&#8203;14826](https://github.com/withastro/astro/pull/14826) [`170f64e`](https://github.com/withastro/astro/commit/170f64e977290b8f9d316b5f283bd03bae33ddde) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the `experimental.failOnPrerenderConflict` flag and replaces it with a new configuration option `prerenderConflictBehavior` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#experimental-flags))

- [#&#8203;14923](https://github.com/withastro/astro/pull/14923) [`95a1969`](https://github.com/withastro/astro/commit/95a1969a05cc9c15f16dcf2177532882bb392581) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Deprecates `astro:schema` and `z` from `astro:content` in favor of `astro/zod` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#deprecated-astroschema-and-z-from-astrocontent))

- [#&#8203;14844](https://github.com/withastro/astro/pull/14844) [`8d43b1d`](https://github.com/withastro/astro/commit/8d43b1d678eed5be85f99b939d55346824c03cb5) Thanks [@&#8203;trueberryless](https://github.com/trueberryless)! - Removes exposed `astro:actions` internals - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-exposed-astroactions-internals))

- [#&#8203;14306](https://github.com/withastro/astro/pull/14306) [`141c4a2`](https://github.com/withastro/astro/commit/141c4a26419fe5bb4341953ea5a0a861d9b398c0) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Changes the shape of `SSRManifest` properties and adds several new required properties in the Adapter API - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-ssrmanifest-interface-structure-adapter-api))

- [#&#8203;15266](https://github.com/withastro/astro/pull/15266) [`f7c9365`](https://github.com/withastro/astro/commit/f7c9365d92b2196d4ba6cffd01b01967ca73728c) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Allows `Astro.csp` and `context.csp` to be undefined instead of throwing errors when `csp: true` is not configured

  When using the experimental Content Security Policy feature in Astro 5.x, `context.csp` was always defined but would throw if `experimental.csp` was not enabled in the Astro config.

  For the stable version of this API in Astro 6, `context.csp` can now be undefined if CSP is not enabled and its methods will never throw.

##### What should I do?

If you were using experimental CSP runtime utilities, you must now access methods conditionally:

```diff
-Astro.csp.insertDirective("default-src 'self'");
+Astro.csp?.insertDirective("default-src 'self'");
```

- [#&#8203;14445](https://github.com/withastro/astro/pull/14445) [`ecb0b98`](https://github.com/withastro/astro/commit/ecb0b98396f639d830a99ddb5895ab9223e4dc87) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Astro v6.0 upgrades to Vite v7.0 as the development server and production bundler - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#vite-70))

- [#&#8203;15407](https://github.com/withastro/astro/pull/15407) [`aedbbd8`](https://github.com/withastro/astro/commit/aedbbd818628bed0533cdd627b73e2c5365aa17e) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Changes how styles of responsive images are emitted - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-how-responsive-image-styles-are-emitted))

- [#&#8203;14306](https://github.com/withastro/astro/pull/14306) [`141c4a2`](https://github.com/withastro/astro/commit/141c4a26419fe5bb4341953ea5a0a861d9b398c0) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Changes integration hooks and HMR access patterns in the Integration API - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-integration-hooks-and-hmr-access-patterns-integration-api))

- [#&#8203;14306](https://github.com/withastro/astro/pull/14306) [`141c4a2`](https://github.com/withastro/astro/commit/141c4a26419fe5bb4341953ea5a0a861d9b398c0) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Removes the unused `astro:ssr-manifest` virtual module - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-astrossr-manifest-virtual-module-integration-api))

- [#&#8203;14485](https://github.com/withastro/astro/pull/14485) [`6f67c6e`](https://github.com/withastro/astro/commit/6f67c6eef2647ef1a1eab78a65a906ab633974bb) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates `import.meta.env` values to always be inlined - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-importmetaenv-values-are-always-inlined))

- [#&#8203;14480](https://github.com/withastro/astro/pull/14480) [`36a461b`](https://github.com/withastro/astro/commit/36a461bf3f64c467bc52aecf511cd831d238e18b) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates `<script>` and `<style>` tags to render in the order they are defined - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#changed-script-and-style-tags-are-rendered-in-the-order-they-are-defined))

- [#&#8203;14407](https://github.com/withastro/astro/pull/14407) [`3bda3ce`](https://github.com/withastro/astro/commit/3bda3ce4edcb1bd1349890c6ed8110f05954c791) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Removes legacy content collection support - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#removed-legacy-content-collections))

##### Minor Changes

- [#&#8203;14306](https://github.com/withastro/astro/pull/14306) [`141c4a2`](https://github.com/withastro/astro/commit/141c4a26419fe5bb4341953ea5a0a861d9b398c0) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Adds new optional properties to `setAdapter()` for adapter entrypoint handling in the Adapter API

  **Changes:**

  - New optional properties:
    - `entryType?: 'self' | 'legacy-dynamic'` - determines if the adapter provides its own entrypoint (`'self'`) or if Astro constructs one (`'legacy-dynamic'`, default)

  **Migration:** Adapter authors can optionally add these properties to support custom dev entrypoints. If not specified, adapters will use the legacy behavior.

- [#&#8203;15700](https://github.com/withastro/astro/pull/15700) [`4e7f3e8`](https://github.com/withastro/astro/commit/4e7f3e8e6849c314a0ab031ebd7f23fb982f0529) Thanks [@&#8203;ocavue](https://github.com/ocavue)! - Updates the internal logic during SSR by providing additional metadata for UI framework integrations.

- [#&#8203;15231](https://github.com/withastro/astro/pull/15231) [`3928b87`](https://github.com/withastro/astro/commit/3928b879dd35fa4ec4dcf545f1610a0f0a55fdae) Thanks [@&#8203;rururux](https://github.com/rururux)! - Adds a new optional `getRemoteSize()` method to the Image Service API.

  Previously, `inferRemoteSize()` had a fixed implementation that fetched the entire image to determine its dimensions.
  With this new helper function that extends `inferRemoteSize()`, you can now override or extend how remote image metadata is retrieved.

  This enables use cases such as:

  - Caching: Storing image dimensions in a database or local cache to avoid redundant network requests.
  - Provider APIs: Using a specific image provider's API (like Cloudinary or Vercel) to get dimensions without downloading the file.

  For example, you can add a simple cache layer to your existing image service:

  ```js
  const cache = new Map();

  const myService = {
    ...baseService,
    async getRemoteSize(url, imageConfig) {
      if (cache.has(url)) return cache.get(url);

      const result = await baseService.getRemoteSize(url, imageConfig);
      cache.set(url, result);
      return result;
    },
  };
  ```

  See the [Image Services API reference documentation](https://docs.astro.build/en/reference/image-service-reference/#getremotesize) for more information.

- [#&#8203;15077](https://github.com/withastro/astro/pull/15077) [`a164c77`](https://github.com/withastro/astro/commit/a164c77336059f2dc3e7f7fe992aa754ed145ef3) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Updates the Integration API to add `setPrerenderer()` to the `astro:build:start` hook, allowing adapters to provide custom prerendering logic.

  The new API accepts either an `AstroPrerenderer` object directly, or a factory function that receives the default prerenderer:

  ```js
  'astro:build:start': ({ setPrerenderer }) => {
    setPrerenderer((defaultPrerenderer) => ({
      name: 'my-prerenderer',
      async setup() {
        // Optional: called once before prerendering starts
      },
      async getStaticPaths() {
        // Returns array of { pathname: string, route: RouteData }
        return defaultPrerenderer.getStaticPaths();
      },
      async render(request, { routeData }) {
        // request: Request
        // routeData: RouteData
        // Returns: Response
      },
      async teardown() {
        // Optional: called after all pages are prerendered
      }
    }));
  }
  ```

  Also adds the `astro:static-paths` virtual module, which exports a `StaticPaths` class for adapters to collect all prerenderable paths from within their target runtime. This is useful when implementing a custom prerenderer that runs in a non-Node environment:

  ```js
  // In your adapter's request handler (running in target runtime)
  import { App } from 'astro/app';
  import { StaticPaths } from 'astro:static-paths';

  export function createApp(manifest) {
    const app = new App(manifest);

    return {
      async fetch(request) {
        const { pathname } = new URL(request.url);

        // Expose endpoint for prerenderer to get static paths
        if (pathname === '/__astro_static_paths') {
          const staticPaths = new StaticPaths(app);
          const paths = await staticPaths.getAll();
          return new Response(JSON.stringify({ paths }));
        }

        // Normal request handling
        return app.render(request);
      },
    };
  }
  ```

  See the [adapter reference](https://docs.astro.build/en/reference/adapter-reference/#custom-prerenderer) for more details on implementing a custom prerenderer.

- [#&#8203;15345](https://github.com/withastro/astro/pull/15345) [`840fbf9`](https://github.com/withastro/astro/commit/840fbf9e4abc7f847e23da8d67904ffde4d95fff) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds a new `emitClientAsset` function to `astro/assets/utils` for integration authors. This function allows emitting assets that will be moved to the client directory during SSR builds, useful for assets referenced in server-rendered content that need to be available on the client.

  ```ts
  import { emitClientAsset } from 'astro/assets/utils';

  // Inside a Vite plugin's transform or load hook
  const handle = emitClientAsset(this, {
    type: 'asset',
    name: 'my-image.png',
    source: imageBuffer,
  });
  ```

- [#&#8203;15460](https://github.com/withastro/astro/pull/15460) [`ee7e53f`](https://github.com/withastro/astro/commit/ee7e53f9de2338517e149895efd26fca44ad80b6) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates the Adapter API to allow providing a `serverEntrypoint` when using `entryType: 'self'`

  Astro 6 introduced a new powerful yet simple Adapter API for defining custom server entrypoints. You can now call `setAdapter()` with the `entryType: 'self'` option and specify your custom `serverEntrypoint`:

  ```js
  export function myAdapter() {
    return {
      name: 'my-adapter',
      hooks: {
        'astro:config:done': ({ setAdapter }) => {
          setAdapter({
            name: 'my-adapter',
            entryType: 'self',
            serverEntrypoint: 'my-adapter/server.js',
            supportedAstroFeatures: {
              // ...
            },
          });
        },
      },
    };
  }
  ```

  If you need further customization at the Vite level, you can omit `serverEntrypoint` and instead specify your custom server entrypoint with [`vite.build.rollupOptions.input`](https://rollupjs.org/configuration-options/#input).

- [#&#8203;15781](https://github.com/withastro/astro/pull/15781) [`2de969d`](https://github.com/withastro/astro/commit/2de969d1f5279d2d0f3024208146f9cd895267b6) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Adds a new `clientAddress` option to the `createContext()` function

  Providing this value gives adapter and middleware authors explicit control over the client IP address. When not provided, accessing `clientAddress` throws an error consistent with other contexts where it is not set by the adapter.

  Additionally, both of the official Netlify and Vercel adapters have been updated to provide this information in their edge middleware.

  ```js
  import { createContext } from 'astro/middleware';

  createContext({
    clientAddress: context.headers.get('x-real-ip'),
  });
  ```

- [#&#8203;15258](https://github.com/withastro/astro/pull/15258) [`d339a18`](https://github.com/withastro/astro/commit/d339a182b387a7a1b0d5dd0d67a0638aaa2b4262) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Stabilizes the adapter feature `experimentalStatiHeaders`. If you were using this feature in any of the supported adapters, you'll need to change the name of the flag:

  ```diff
  export default defineConfig({
    adapter: netlify({
  -    experimentalStaticHeaders: true
  +    staticHeaders: true
    })
  })
  ```

- [#&#8203;15535](https://github.com/withastro/astro/pull/15535) [`dfe2e22`](https://github.com/withastro/astro/commit/dfe2e22042f92172442ab32777b3cce90685b76a) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Exports new `createRequest()` and `writeResponse()` utilities from `astro/app/node`

  To replace the deprecated `NodeApp.createRequest()` and `NodeApp.writeResponse()` methods, the `astro/app/node` module now exposes new `createRequest()` and `writeResponse()` utilities. These can be used to convert a NodeJS `IncomingMessage` into a web-standard `Request` and stream a web-standard `Response` into a NodeJS `ServerResponse`:

  ```js
  import { createApp } from 'astro/app/entrypoint';
  import { createRequest, writeResponse } from 'astro/app/node';
  import { createServer } from 'node:http';

  const app = createApp();

  const server = createServer(async (req, res) => {
    const request = createRequest(req);
    const response = await app.render(request);
    await writeResponse(response, res);
  });
  ```

- [#&#8203;15755](https://github.com/withastro/astro/pull/15755) [`f9ee868`](https://github.com/withastro/astro/commit/f9ee8685dd26e9afeba3b48d41ad6714f624b12f) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds a new `security.serverIslandBodySizeLimit` configuration option

  Server island POST endpoints now enforce a body size limit, similar to the existing `security.actionBodySizeLimit` for Actions. The new option defaults to `1048576` (1 MB) and can be configured independently.

  Requests exceeding the limit are rejected with a 413 response. You can customize the limit in your Astro config:

  ```js
  export default defineConfig({
    security: {
      serverIslandBodySizeLimit: 2097152, // 2 MB
    },
  });
  ```

- [#&#8203;15529](https://github.com/withastro/astro/pull/15529) [`a509941`](https://github.com/withastro/astro/commit/a509941a7a7a1e53f402757234bb88e5503e5119) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds a new build-in font provider `npm` to access fonts installed as NPM packages

  You can now add web fonts specified in your `package.json` through Astro's type-safe Fonts API. The `npm` font provider allows you to add fonts either from locally installed packages in `node_modules` or from a CDN.

  Set `fontProviders.npm()` as your fonts provider along with the required `name` and `cssVariable` values, and add `options` as needed:

  ```js
  import { defineConfig, fontProviders } from 'astro/config';

  export default defineConfig({
    experimental: {
      fonts: [
        {
          name: 'Roboto',
          provider: fontProviders.npm(),
          cssVariable: '--font-roboto',
        },
      ],
    },
  });
  ```

  See the [NPM font provider reference documentation](https://docs.astro.build/en/reference/font-provider-reference/#npm) for more details.

- [#&#8203;15471](https://github.com/withastro/astro/pull/15471) [`32b4302`](https://github.com/withastro/astro/commit/32b430213bbe51898b77ec2eadbacb9dfb220f75) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Adds a new experimental flag `queuedRendering` to enable a queue-based rendering engine

  The new engine is based on a two-pass process, where the first pass
  traverses the tree of components, emits an ordered queue, and then the queue is rendered.

  The new engine does not use recursion, and comes with two customizable options.

  Early benchmarks showed significant speed improvements and memory efficiency in big projects.

##### Queue-rendered based

The new engine can be enabled in your Astro config with `experimental.queuedRendering.enabled` set to `true`, and can be further customized with additional sub-features.

```js
// astro.config.mjs
export default defineConfig({
  experimental: {
    queuedRendering: {
      enabled: true,
    },
  },
});
```

##### Pooling

With the new engine enabled, you now have the option to have a pool of nodes that can be saved and reused across page rendering. Node pooling has no effect when rendering pages on demand (SSR) because these rendering requests don't share memory. However, it can be very useful for performance when building static pages.

```js
// astro.config.mjs
export default defineConfig({
  experimental: {
    queuedRendering: {
      enabled: true,
      poolSize: 2000, // store up to 2k nodes to be reused across renderers
    },
  },
});
```

##### Content caching

The new engine additionally unlocks a new `contentCache` option. This allows you to cache values of nodes during the rendering phase. This is currently a boolean feature with no further customization (e.g. size of cache) that uses sensible defaults for most large content collections:

When disabled, the pool engine won't cache strings, but only types.

```js
// astro.config.mjs
export default defineConfig({
  experimental: {
    queuedRendering: {
      enabled: true,
      contentCache: true, // enable re-use of node values
    },
  },
});
```

For more information on enabling and using this feature in your project, see the [experimental queued rendering docs](https://docs.astro.build/en/reference/experimental-flags/queued-rendering/) for more details.

- [#&#8203;14888](https://github.com/withastro/astro/pull/14888) [`4cd3fe4`](https://github.com/withastro/astro/commit/4cd3fe412bddbb0deb1383e83f3f8ae6e72596af) Thanks [@&#8203;OliverSpeir](https://github.com/OliverSpeir)! - Updates `astro add cloudflare` to better setup types, by adding `./worker-configuration.d.ts` to tsconfig includes and a `generate-types` script to package.json

- [#&#8203;15646](https://github.com/withastro/astro/pull/15646) [`0dd9d00`](https://github.com/withastro/astro/commit/0dd9d00cf8be38c53217426f6b0e155a6f7c2a22) Thanks [@&#8203;delucis](https://github.com/delucis)! - Removes redundant `fetchpriority` attributes from the output of Astro’s `<Image>` component

  Previously, Astro would always include `fetchpriority="auto"` on images not using the `priority` attribute.
  However, this is the default value, so specifying it is redundant. This change omits the attribute by default.

- [#&#8203;15291](https://github.com/withastro/astro/pull/15291) [`89b6cdd`](https://github.com/withastro/astro/commit/89b6cdd4075f5c9362291c386bb1e7c100b467a5) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes the `experimental.fonts` flag and replaces it with a new configuration option `fonts` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#experimental-flags))

- [#&#8203;15495](https://github.com/withastro/astro/pull/15495) [`5b99e90`](https://github.com/withastro/astro/commit/5b99e9077a92602f1e46e9b6eb9094bcd00c640e) Thanks [@&#8203;leekeh](https://github.com/leekeh)! - Adds a new `middlewareMode` adapter feature to replace the previous `edgeMiddleware` option.

  This feature only impacts adapter authors. If your adapter supports `edgeMiddleware`, you should upgrade to the new `middlewareMode` option to specify the middleware mode for your adapter as soon as possible. The `edgeMiddleware` feature is deprecated and will be removed in a future major release.

  ```diff
  export default function createIntegration() {
    return {
      name: '@&#8203;example/my-adapter',
      hooks: {
        'astro:config:done': ({ setAdapter }) => {
          setAdapter({
            name: '@&#8203;example/my-adapter',
            serverEntrypoint: '@&#8203;example/my-adapter/server.js',
            adapterFeatures: {
  -            edgeMiddleware: true
  +            middlewareMode: 'edge'
            }
          });
        },
      },
    };
  }
  ```

- [#&#8203;15694](https://github.com/withastro/astro/pull/15694) [`66449c9`](https://github.com/withastro/astro/commit/66449c930e73e9a58ce547b9c32635a98a310966) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds `preserveBuildClientDir` option to adapter features

  Adapters can now opt in to preserving the client/server directory structure for static builds by setting `preserveBuildClientDir: true` in their adapter features. When enabled, static builds will output files to `build.client` instead of directly to `outDir`.

  This is useful for adapters that require a consistent directory structure regardless of the build output type, such as deploying to platforms with specific file organization requirements.

  ```js
  // my-adapter/index.js
  export default function myAdapter() {
    return {
      name: 'my-adapter',
      hooks: {
        'astro:config:done': ({ setAdapter }) => {
          setAdapter({
            name: 'my-adapter',
            adapterFeatures: {
              buildOutput: 'static',
              preserveBuildClientDir: true,
            },
          });
        },
      },
    };
  }
  ```

- [#&#8203;15332](https://github.com/withastro/astro/pull/15332) [`7c55f80`](https://github.com/withastro/astro/commit/7c55f80fa1fd91f8f71ad60437f81e6c7f98f69d) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds a `fileURL` option to `renderMarkdown` in content loaders, enabling resolution of relative image paths. When provided, relative image paths in markdown will be resolved relative to the specified file URL and included in `metadata.localImagePaths`.

  ```js
  const loader = {
    name: 'my-loader',
    load: async ({ store, renderMarkdown }) => {
      const content = `
  # My Post

  ![Local image](./image.png)
  `;
      // Provide a fileURL to resolve relative image paths
      const fileURL = new URL('./posts/my-post.md', import.meta.url);
      const rendered = await renderMarkdown(content, { fileURL });
      // rendered.metadata.localImagePaths now contains the resolved image path
    },
  };
  ```

- [#&#8203;15407](https://github.com/withastro/astro/pull/15407) [`aedbbd8`](https://github.com/withastro/astro/commit/aedbbd818628bed0533cdd627b73e2c5365aa17e) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Adds support for responsive images when `security.csp` is enabled, out of the box.

  Astro's implementation of responsive image styles has been updated to be compatible with a configured Content Security Policy.

  Instead of, injecting style elements at runtime, Astro will now generate your styles at build time using a combination of `class=""` and `data-*` attributes. This means that your processed styles are loaded and hashed out of the box by Astro.

  If you were previously choosing between Astro's CSP feature and including responsive images on your site, you may now use them together.

- [#&#8203;15543](https://github.com/withastro/astro/pull/15543) [`d43841d`](https://github.com/withastro/astro/commit/d43841dfa8998c4dc1a510a2b120fedbd9ce77c6) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Adds a new `experimental.rustCompiler` flag to opt into the experimental Rust-based Astro compiler

  This experimental compiler is faster, provides better error messages, and generally has better support for modern JavaScript, TypeScript, and CSS features.

  After enabling in your Astro config, the `@astrojs/compiler-rs` package must also be installed into your project separately:

  ```js
  import { defineConfig } from 'astro/config';

  export default defineConfig({
    experimental: {
      rustCompiler: true,
    },
  });
  ```

  This new compiler is still in early development and may exhibit some differences compared to the existing Go-based compiler. Notably, this compiler is generally more strict in regard to invalid HTML syntax and may throw errors in cases where the Go-based compiler would have been more lenient. For example, unclosed tags (e.g. `<p>My paragraph`) will now result in errors.

  For more information about using this experimental feature in your project, especially regarding expected differences and limitations, please see the [experimental Rust compiler reference docs](https://docs.astro.build/en/reference/experimental-flags/rust-compiler/). To give feedback on the compiler, or to keep up with its development, see the [RFC for a new compiler for Astro](https://github.com/withastro/roadmap/discussions/1306) for more information and discussion.

- [#&#8203;15349](https://github.com/withastro/astro/pull/15349) [`a257c4c`](https://github.com/withastro/astro/commit/a257c4c3c7f0cdc5089b522ed216401d46d214c9) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Passes collection name to live content loaders

  Live content collection loaders now receive the collection name as part of their parameters. This is helpful for loaders that manage multiple collections or need to differentiate behavior based on the collection being accessed.

  ```ts
  export function storeLoader({ field, key }) {
    return {
      name: 'store-loader',
      loadCollection: async ({ filter, collection }) => {
        // ...
      },
      loadEntry: async ({ filter, collection }) => {
        // ...
      },
    };
  }
  ```

- [#&#8203;15006](https://github.com/withastro/astro/pull/15006) [`f361730`](https://github.com/withastro/astro/commit/f361730bc820c01a2ec3e508ac940be8077d8c04) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds new session driver object shape

  For greater flexibility and improved consistency with other Astro code, session drivers are now specified as an object:

  ```diff
  -import { defineConfig } from 'astro/config'
  +import { defineConfig, sessionDrivers } from 'astro/config'

  export default defineConfig({
    session: {
  -    driver: 'redis',
  -    options: {
  -      url: process.env.REDIS_URL
  -    },
  +    driver: sessionDrivers.redis({
  +      url: process.env.REDIS_URL
  +    }),
    }
  })
  ```

  Specifying the session driver as a string has been deprecated, but will continue to work until this feature is removed completely in a future major version. The object shape is the current recommended and documented way to configure a session driver.

- [#&#8203;15291](https://github.com/withastro/astro/pull/15291) [`89b6cdd`](https://github.com/withastro/astro/commit/89b6cdd4075f5c9362291c386bb1e7c100b467a5) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds a new Fonts API to provide first-party support for adding custom fonts in Astro.

  This feature allows you to use fonts from both your file system and several built-in supported providers (e.g. Google, Fontsource, Bunny) through a unified API. Keep your site performant thanks to sensible defaults and automatic optimizations including preloading and fallback font generation.

  To enable this feature, configure `fonts` with one or more fonts:

  ```js title="astro.config.mjs"
  import { defineConfig, fontProviders } from 'astro/config';

  export default defineConfig({
    fonts: [
      {
        provider: fontProviders.fontsource(),
        name: 'Roboto',
        cssVariable: '--font-roboto',
      },
    ],
  });
  ```

  Import and include the `<Font />` component with the required `cssVariable` property in the head of your page, usually in a dedicated `Head.astro` component or in a layout component directly:

  ```astro
  ---
  // src/layouts/Layout.astro
  import { Font } from 'astro:assets';
  ---

  <html>
    <head>
      <Font cssVariable="--font-roboto" preload />
    </head>
    <body>
      <slot />
    </body>
  </html>
  ```

  In any page rendered with that layout, including the layout component itself, you can now define styles with your font's `cssVariable` to apply your custom font.

  In the following example, the `<h1>` heading will have the custom font applied, while the paragraph `<p>` will not.

  ```astro
  ---
  // src/pages/example.astro
  import Layout from '../layouts/Layout.astro';
  ---

  <Layout>
    <h1>In a galaxy far, far away...</h1>

    <p>Custom fonts make my headings much cooler!</p>

    <style>
      h1 {
        font-family: var('--font-roboto');
      }
    </style>
  </Layout>
  ```

  Visit the updated [fonts guide](https://docs.astro.build/en/guides/fonts/) to learn more about adding custom fonts to your project.

- [#&#8203;14550](https://github.com/withastro/astro/pull/14550) [`9c282b5`](https://github.com/withastro/astro/commit/9c282b5e6d3f0d678bc478a863e883fa4765dd17) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Adds support for live content collections

  Live content collections are a new type of [content collection](https://docs.astro.build/en/guides/content-collections/) that fetch their data at runtime rather than build time. This allows you to access frequently updated data from CMSs, APIs, databases, or other sources using a unified API, without needing to rebuild your site when the data changes.

##### Live collections vs build-time collections

In Astro 5.0, the content layer API added support for adding diverse content sources to content collections. You can create loaders that fetch data from any source at build time, and then access it inside a page via `getEntry()` and `getCollection()`. The data is cached between builds, giving fast access and updates.

However, there was no method for updating the data store between builds, meaning any updates to the data needed a full site deploy, even if the pages are rendered on demand. This meant that content collections were not suitable for pages that update frequently. Instead, these pages tended to access the APIs directly in the frontmatter. This worked, but it led to a lot of boilerplate, and meant users didn't benefit from the simple, unified API that content loaders offer. In most cases, users tended to individually create loader libraries shared between pages.

Live content collections ([introduced experimentally in Astro 5.10](https://astro.build/blog/live-content-collections-deep-dive/)) solve this problem by allowing you to create loaders that fetch data at runtime, rather than build time. This means that the data is always up-to-date, without needing to rebuild the site.

##### How to use

To use live collections, create a new `src/live.config.ts` file (alongside your `src/content.config.ts` if you have one) to define your live collections with a live content loader using the new `defineLiveCollection()` function from the `astro:content` module:

```ts title="src/live.config.ts"
import { defineLiveCollection } from 'astro:content';
import { storeLoader } from '@&#8203;mystore/astro-loader';

const products = defineLiveCollection({
  loader: storeLoader({
    apiKey: process.env.STORE_API_KEY,
    endpoint: 'https://api.mystore.com/v1',
  }),
});

export const collections = { products };
```

You can then use the `getLiveCollection()` and `getLiveEntry()` functions to access your live data, along with error handling (since anything can happen when requesting live data!):

```astro
---
import { getLiveCollection, getLiveEntry, render } from 'astro:content';
// Get all products
const { entries: allProducts, error } = await getLiveCollection('products');
if (error) {
  // Handle error appropriately
  console.error(error.message);
}

// Get products with a filter (if supported by your loader)
const { entries: electronics } = await getLiveCollection('products', { category: 'electronics' });

// Get a single product by ID (string syntax)
const { entry: product, error: productError } = await getLiveEntry('products', Astro.params.id);
if (productError) {
  return Astro.redirect('/404');
}

// Get a single product with a custom query (if supported by your loader) using a filter object
const { entry: productBySlug } = await getLiveEntry('products', { slug: Astro.params.slug });
const { Content } = await render(product);
---

<h1>{product.data.title}</h1>
<Content />
```

##### Upgrading from experimental live collections

If you were using the experimental feature, you must remove the `experimental.liveContentCollections` flag from your `astro.config.*` file:

```diff
 export default defineConfig({
   // ...
-  experimental: {
-    liveContentCollections: true,
-  },
 });
```

No other changes to your project code are required as long as you have been keeping up with Astro 5.x patch releases, which contained breaking changes to this experimental feature. If you experience problems with your live collections after upgrading to Astro v6 and removing this flag, please review the [Astro CHANGELOG from 5.10.2](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#5102) onwards for any potential updates you might have missed, or follow the [current v6 documentation for live collections](https://docs.astro.build/en/guides/content-collections/).

- [#&#8203;15548](https://github.com/withastro/astro/pull/15548) [`5b8f573`](https://github.com/withastro/astro/commit/5b8f5737feb1a051b7cbd5d543dd230492e5211f) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds a new optional `embeddedLangs` prop to the `<Code />` component to support languages beyond the primary `lang`

  This allows, for example, highlighting `.vue` files with a `<script setup lang="tsx">` block correctly:

  ```astro
  ---
  import { Code } from 'astro:components';

  const code = `
  <script setup lang="tsx">
  const Text = ({ text }: { text: string }) => <div>{text}</div>;
  </script>

  <template>
    <Text text="hello world" />
  </template>`;
  ---

  <Code {code} lang="vue" embeddedLangs={['tsx']} />
  ```

  See the [`<Code />` component documentation](https://docs.astro.build/en/guides/syntax-highlighting/#code-) for more details.

- [#&#8203;14826](https://github.com/withastro/astro/pull/14826) [`170f64e`](https://github.com/withastro/astro/commit/170f64e977290b8f9d316b5f283bd03bae33ddde) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds an option `prerenderConflictBehavior` to configure the behavior of conflicting prerendered routes

  By default, Astro warns you during the build about any conflicts between multiple dynamic routes that can result in the same output path. For example `/blog/[slug]` and `/blog/[...all]` both could try to prerender the `/blog/post-1` path. In such cases, Astro renders only the [highest priority route](https://docs.astro.build/en/guides/routing/#route-priority-order) for the conflicting path. This allows your site to build successfully, although you may discover that some pages are rendered by unexpected routes.

  With the new `prerenderConflictBehavior` configuration option, you can now configure this further:

  - `prerenderConflictBehavior: 'error'` fails the build
  - `prerenderConflictBehavior: 'warn'` (default) logs a warning and the highest-priority route wins
  - `prerenderConflictBehavior: 'ignore'` silently picks the highest-priority route when conflicts occur

  ```diff
  import { defineConfig } from 'astro/config';

  export default defineConfig({
  +    prerenderConflictBehavior: 'error',
  });
  ```

- [#&#8203;14946](https://github.com/withastro/astro/pull/14946) [`95c40f7`](https://github.com/withastro/astro/commit/95c40f7109ce240206c3951761a7bb439dd809cb) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Removes the `experimental.csp` flag and replaces it with a new configuration option `security.csp` - ([v6 upgrade guidance](https://docs.astro.build/en/guides/upgrade-to/v6/#experimental-flags))

- [#&#8203;15579](https://github.com/withastro/astro/pull/15579) [`08437d5`](https://github.com/withastro/astro/commit/08437d531e31b79a42333a9f7aabaa9fe646ce4f) Thanks [@&#8203;ascorbic](https://github.com/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:

```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@&#8203;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:

```astro
---
// 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`:

```ts
// 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()`:

```ts
// 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:

```js
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`:

```ts
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](https://docs.astro.build/en/reference/experimental-flags/route-caching/).
For a complete overview and to give feedback on this experimental API, see the [Route Caching RFC](https://github.com/withastro/roadmap/pull/1245).

- [#&#8203;15483](https://github.com/withastro/astro/pull/15483) [`7be3308`](https://github.com/withastro/astro/commit/7be3308bf4b1710a3f378ba338d09a8528e01e76) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds `streaming` option to the `createApp()` function in the Adapter API, mirroring the same functionality available when creating a new `App` instance

  An adapter's `createApp()` function now accepts `streaming` (defaults to `true`) as an option. HTML streaming breaks a document into chunks to send over the network and render on the page in order. This normally results in visitors seeing your HTML as fast as possible but factors such as network conditions and waiting for data fetches can block page rendering.

  HTML streaming helps with performance and generally provides a better visitor experience. In most cases, disabling streaming is not recommended.

  However, when you need to disable HTML streaming (e.g. your host only supports non-streamed HTML caching at the CDN level), you can opt out of the default behavior by passing `streaming: false` to `createApp()`:

  ```ts
  import { createApp } from 'astro/app/entrypoint';

  const app = createApp({ streaming: false });
  ```

  See more about [the `createApp()` function](https://docs.astro.build/en/reference/adapter-reference/#createapp) in the Adapter API reference.

##### Patch Changes

- [#&#8203;15423](https://github.com/withastro/astro/pull/15423) [`c5ea720`](https://github.com/withastro/astro/commit/c5ea720261a35324988147fbf69d8200e496e1d0) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves error message when a dynamic redirect destination does not match any existing route.

  Previously, configuring a redirect like `/categories/[category]` → `/categories/[category]/1` in static output mode would fail with a misleading "getStaticPaths required" error. Now, Astro detects this early and provides a clear error explaining that the destination does not match any existing route.

- [#&#8203;15167](https://github.com/withastro/astro/pull/15167) [`4fca170`](https://github.com/withastro/astro/commit/4fca1701eca1d107df43ef280cab342dfdacbb44) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes an issue where CSS from unused components, when using content collections, could be incorrectly included between page navigations in development mode.

- [#&#8203;15565](https://github.com/withastro/astro/pull/15565) [`30cd6db`](https://github.com/withastro/astro/commit/30cd6dbebe771efb6f71dcff7e6b44026fad6797) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes an issue where the use of the Astro internal logger couldn't work with Cloudflare Vite plugin.

- [#&#8203;15508](https://github.com/withastro/astro/pull/15508) [`2c6484a`](https://github.com/withastro/astro/commit/2c6484a4c34e86b8a26342a48986da26768de27b) Thanks [@&#8203;KTibow](https://github.com/KTibow)! - Fixes behavior when shortcuts are used before server is ready

- [#&#8203;15125](https://github.com/withastro/astro/pull/15125) [`6feb0d7`](https://github.com/withastro/astro/commit/6feb0d7bec1e333eb795ae0fc51516182a73eb2b) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Improves JSDoc annotations for `AstroGlobal`, `AstroSharedContext` and `APIContext` types

- [#&#8203;15712](https://github.com/withastro/astro/pull/15712) [`7ac43c7`](https://github.com/withastro/astro/commit/7ac43c713be0c69b8df0fdaaca1e85e022361216) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Improves `astro info` by supporting more operating systems when copying the information to the clipboard.

- [#&#8203;15054](https://github.com/withastro/astro/pull/15054) [`22db567`](https://github.com/withastro/astro/commit/22db567d4cea7a4476271c101c452a2624b7d996) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves zod union type error messages to show expected vs received types instead of generic "Invalid input"

- [#&#8203;15064](https://github.com/withastro/astro/pull/15064) [`caf5621`](https://github.com/withastro/astro/commit/caf5621b324344fc0d46fb462c88c0d79fccca6b) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Fixes a bug that caused incorrect warnings of duplicate entries to be logged by the glob loader when editing a file

- [#&#8203;15801](https://github.com/withastro/astro/pull/15801) [`01db4f3`](https://github.com/withastro/astro/commit/01db4f37ddc14e2148df8390e0c0c600677a2417) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Improves the experience of working with experimental route caching in dev mode by replacing some errors with silent no-ops, avoiding the need to write conditional logic to handle different modes

  Adds a `cache.enabled` property to `CacheLike` so libraries can check whether caching is active without try/catch.

- [#&#8203;15562](https://github.com/withastro/astro/pull/15562) [`e14a51d`](https://github.com/withastro/astro/commit/e14a51d30196bad534bacb14aac7033b91aed741) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Removes types for the `astro:ssr-manifest` module, which was removed

- [#&#8203;15542](https://github.com/withastro/astro/pull/15542) [`9760404`](https://github.com/withastro/astro/commit/97604040b73ec1d029f5d5a489aa744aaecfd173) T…
@serhalp
Copy link

serhalp commented Mar 23, 2026

👋🏼 This sounds good from the Netlify side

:shipit:

One minor piece of feedback: the semantics of invalidate() could be more precisely specified: is it intended to be a "hard purge" or a "soft purge"? in the case of a stale-while-revalidate cache entry, does invalidate() imply a background _re_validation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants