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. CallkeepPrivateWhenTested on that route’s response headers, after the loader’s reads:
Server (loader)
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
ReadinggetClientState() 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 does, not in a loader. The attributes describe the visitor.
A feature test on a cached route
A feature test renders a version per test group, so a route that renders one must not be shared either. CallkeepPrivateWhenTested on it as above.