> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abconvert.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Hydrogen full-page cache

> Keep a route that Oxygen's full-page cache stores from serving one visitor's test group to everyone.

Oxygen does not cache full pages unless a route opts in to its [full-page cache](https://shopify.dev/docs/storefronts/headless/hydrogen/caching/full-page-cache). If none of your routes opts in, you need nothing from this page.

## Keep tested routes private

A route a test can change can hold one visitor's test price, offer, or free-shipping bar, or leave them out for a visitor the test excludes, so a route that opts in must not share it. Call `keepPrivateWhenTested` on that route's response headers, after the loader's reads:

```ts Server (loader) theme={null}
// app/routes/products.$handle.tsx
import { keepPrivateWhenTested } from '@abconvert/headless/hydrogen';
import { getABConvert } from '~/lib/abconvert.server';

export async function loader({ request, params }: LoaderFunctionArgs) {
  const abconvert = await getABConvert(request);
  const product = await loadProduct(params.handle);
  for (const variant of product.variants) abconvert.getPriceByVariantId(variant.id);

  // Your route's full-page cache setting
  const headers = new Headers({ 'Cache-Control': 'public, max-age=3600' });
  keepPrivateWhenTested(headers, abconvert);

  return data({ product, abconvert: abconvert.getPageState() }, { headers });
}
```

It turns a `public` `Cache-Control` or `Oxygen-Cache-Control` header into `private` when a test can change the route: the loader read a tested product's price, or offers or shipping rates while such a test runs, a price test prices a product the URL shows, a URL redirect test covers it, or the response sets a cookie. It also makes the route `private` when the server could not load your tests, for example when the first load times out on a cold start, since without them it cannot tell which routes a test changes. A route no test can change stays cached while your tests run. Both Hydrogen entries export it.

While a price test runs, every product, collection, search, and home page counts as one it can change, because any of them can show a tested price. If your product pages show only their own product's price, pass `onlyOwnProductPrices: true` to `createABConvert`, and only the tested products' pages turn private. We trade the full-page cache on those routes, while you test, for the right price and offer for every visitor.

## Keep the visitor out of the page

Reading `getClientState()` in a loader also makes the route private: it holds the visitor. Return `getPageState()` instead. It carries the tests the request rendered, as the visitor saw them, and nothing about the visitor, so a cached route can carry it. The browser reads the visitor from the cookies.

On Hydrogen 2026.10 and later, read the cart attributes in the browser, as [Step 4 of the Hydrogen guide](/headless-sdk/hydrogen#step-4-write-the-cart-attributes) does, not in a loader. The attributes describe the visitor.

## A feature test on a cached route

A [feature test](/headless-sdk/feature-tests) renders a version per test group, so a route that renders one must not be shared either. Call `keepPrivateWhenTested` on it as above.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.