The cart token: the one object a storefront can never serve from cache
In short
A cart is 2 things: an identity token held by the browser, and a document held by the server. The token names the cart, it does not contain it — which is why a cart response is personal to one shopper, can never sit in a shared cache, and why signing in on a second device forces a merge decision that quietly loses items when nobody has written the rule down.
Key takeaways
- The cart token is identity, not contents. The browser holds an id; the server holds the document it names.
- A cart moves through 4 states: created on first add, anonymous, merged at sign-in, retired at order creation.
- Any response whose body depends on the cart id must be uncacheable or keyed on that id — including a server-rendered cart badge.
- Merge by variant with quantity as the maximum, not the sum, or a shopper who added the same item on 2 devices gets 2 of it.
- Carts on a shared device are the failure nobody tests: the cookie outlives the person who created it.
A cart is an identity token plus a server-held document. The browser stores an opaque id, usually in a cookie or in local storage; the server stores the lines, the totals and whatever customer is attached. The token names the cart. It does not contain it, and it is not a session in the login sense — a cart exists long before anybody signs in.
That definition does the explanatory work for the rest of a storefront. Every other page is the same for everyone who asks for it, so it can be built once and served from a cache. The cart differs for every holder of a token, so it cannot. There is no configuration that makes it cacheable — only a choice about where the uncacheable part sits.
The four states a cart moves through
- Created. The first add-to-cart mints an id and writes it to the browser. Nothing exists before that, which is why an empty cart costs the server nothing.
- Anonymous. The document accumulates lines with no customer attached. Most carts live and die entirely in this state.
- Merged. Sign-in produces 2 carts — the anonymous one in this browser and whatever the customer already had — and something has to reconcile them. This is where contents disappear.
- Retired. Order creation ends the cart's life. The order becomes the record, the cart id must be cleared from the browser, and a stale id left in place is how a paid-for basket reappears an hour later.
The same cart, seen from four places
| Situation | Browser | Server | What the shopper sees |
|---|---|---|---|
| First add to cart | A new id, written to a cookie | A cart document with 1 line, no customer | One item |
| Same browser, next day | The same id, if the cookie survived | The same document, if it has not expired | The same item — or nothing, if either side dropped it |
| Second device, not signed in | A different id | A second, unrelated cart | An empty cart, and nothing is wrong |
| Signs in on the second device | The id it already had | 2 carts to reconcile | Whatever your merge rule decided — usually undocumented |
| Shared device, a second person signs in | The first person's id, still in the cookie | An anonymous cart holding somebody else's items | Another household member's basket, merged into theirs |
| Order placed | The id should be cleared | The cart is retired; the order is the record | An empty cart, unless the id was left behind |
Rows 3 and 4 are what support tickets are made of. A shopper who added items on a phone and then opened a laptop has not lost anything: they were never signed in, so 2 browsers held 2 ids and therefore 2 carts. Nothing can be recovered because nothing was shared. Saying so plainly in the interface is cheaper than any amount of engineering.
The merge rule, written down
Sign-in forces a decision with 3 plausible answers, and most builds pick one by accident. Customer-cart-wins silently discards what the shopper just added — the worst outcome, because those items are the most recent and the most intended. Anonymous-cart-wins throws away a cart built deliberately on another day. A union keeps both and needs a quantity rule.
Why nothing on this path can be cached
Two failure classes follow from the definition, and both are caching failures rather than cart failures. The first is a cached cart fragment: a response whose body depends on the cart id, served from a shared cache to whoever asks next. The cart endpoint is the easy half — everyone marks it private. The hard half is a product page that renders a cart-count badge on the server, because that response is now personalised and an edge cache will hand it to the next visitor.
- Any response whose body varies by cart id is either uncacheable or keyed on that id. There is no third option, and 'it works in testing' means the cache was cold.
- Keep the badge on the client. Rendering the cart count client-side is the cheapest way to keep the whole page shareable — the wider trade-off is what belongs on the server and what has to stay on the client.
- The cart round trip cannot be cached, so it can only be shortened. Where that request terminates is a hosting decision: where a custom storefront should run.
- Know which layer you are fighting. Build output, edge, browser and the in-page store fail differently, which is the four caches between you and the shopper.
The token names the cart. It does not contain it. Every cart bug that surprises a team is that sentence, arriving late.
Where the cart stops being the cart
The cart's authority ends at checkout. Once a shopper commits, the totals stop being a live calculation and become terms, and the object carrying the money is a different one with its own scope and expiry: the payment token that carries an allowance. A mismatch between what the cart showed and what the page showed is its own diagnosis: the cart price does not match the page price.
Most of the difficulty in a headless build concentrates in this 1 object, because it is the only place identity, freshness and personalisation arrive together. Writing its states and its merge rule down before the first sprint is MVP and product build work, and it sits at the centre of storefront architecture and platform choice within software for retail and ecommerce.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
Why do cart contents differ between a shopper's phone and their laptop?
Because an anonymous cart is identified by a token stored in one browser, and a second browser has a different token and therefore a different cart. Nothing has been lost and nothing can be recovered — the 2 carts were never connected. They converge only when the shopper signs in on both devices, at which point your merge rule decides what the combined cart contains.
How long should a cart token live?
Long enough to survive a considered purchase and short enough that a shared device does not leak a basket — days rather than months for the browser token, with the server document expiring on its own schedule. Set the 2 lifetimes independently: a browser id that outlives its server document produces the 'my cart vanished' report, and the reverse produces stale carts nobody can explain.
Can a cart response be cached at the edge if we vary on the cart cookie?
Technically yes, practically no. Keying a shared cache on a token that is unique per shopper gives you a cache with a hit rate near zero and an entry per visitor, which costs more than it saves and adds a class of bug where one misconfigured key serves the wrong basket. Mark it private and spend the effort on shortening the round trip instead.
Should the cart be stored on the client instead of the server?
No, not as the source of truth. A client-only cart cannot be repriced, cannot check availability and cannot survive a device change, so every serious storefront ends up with a server document anyway. Keeping a local copy as an optimistic cache for instant feedback is worth doing, provided the server response always wins on conflict.
- headless commerce
- cart
- caching
- storefront architecture
The work behind this page
Builds from our portfolio that this page draws on.
AI Lease Management
AI-powered commercial real estate lease management for multi-brand operators — automates lease data extraction, obligation tracking, and portfolio intelligence.
Real EstateLow Latency Food Ordering Platform
Unified events operations platform: vendor management, order tracking, payments, automated settlements.
MarketplaceRead next
- The public storefront token: what a shopper-side key can readA storefront credential is safe because of its scope, not its secrecy. It ships to the browser by design — so the only question that matters is what it can return.definition
- Four caches sit between your product page and the shopperBuild output, the edge, the browser and the in-page store. You can purge the first two on demand and only expire the other two — which is the rule every caching decision follows.definition
- Your product page's largest element arrives last on mobileHalf the time the largest element on a product page is not the product shot at all. Identify it first, then the fix is either an image-pipeline change or a script-order change.diagnostic
Related across the site
Working on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation