Cache Invalidation for Headless Commerce: On Demand ISR vs Webhook Purge vs Timed Revalidation (2026)
The whole point of a headless storefront is that pages are cached and served in milliseconds. The whole problem is that a cached product page can show a price that changed an hour ago or an in stock badge on something you sold out of. Cache invalidation is the machinery that decides how long the storefront is allowed to lie. There are three strategies teams reach for, each fails in a different way, and the production answer is almost never one of them alone.
Cache Invalidation for Headless Commerce: On Demand ISR vs Webhook Purge vs Timed Revalidation (2026)
A headless storefront earns its speed by not rebuilding a product page every time someone loads it. It renders once, caches the result at the edge or in a build artifact, and serves that cached copy in a few milliseconds to everyone who follows. That is the entire performance story, and it is also the entire correctness problem, because commerce data does not sit still. A merchandiser changes a price, a warehouse ships the last unit, a sale ends at midnight, and until the cache learns about it, the storefront is confidently serving a page that is wrong. Cache invalidation is the machinery that decides how long that lie is allowed to persist, and choosing it badly produces either a slow site or a site that sells things you do not have.
There are three strategies teams actually use, and the mistake is treating them as competitors when they are layers. This post walks each one, the specific way each fails, and why the configuration that survives a real catalog is a hybrid that uses the precise tools for correctness and the blunt one as a safety net.
The Three Strategies At A Glance
| Strategy | How it invalidates | Freshness | Failure mode | Best for |
|---|---|---|---|---|
| On demand ISR | An event calls revalidateTag or revalidatePath, regenerating a specific page |
Seconds after the event | Missed event leaves a page stale until the next timed pass | Framework native storefronts on Next.js or similar |
| Webhook driven purge | A Shopify webhook triggers a function that purges CDN entries by URL or cache tag | Seconds after the event | Missed or delayed webhook, thundering herd on a hot page | CDN centric setups with surrogate key support |
| Timed revalidation | The cache expires on a TTL, often serving stale while it refreshes in the background | Bounded by the TTL window | Guaranteed staleness for the length of the window | Slow moving content, safety net under the other two |
The columns that decide everything are freshness and failure mode. The two event driven strategies, on demand ISR and webhook purge, are precise: they update exactly the pages that changed, within seconds, and touch nothing else. Their shared weakness is that they depend on an event arriving. Timed revalidation is the opposite. It needs no event and therefore cannot miss one, but it pays for that reliability with a guaranteed window during which every page is allowed to be wrong. You do not pick the most reliable column or the freshest column. You combine them so the precise tools do the work and the blunt tool catches what they drop.
On Demand ISR: Precise, and Only as Good as the Trigger
Incremental Static Regeneration with on demand revalidation is the framework native answer. Your storefront tags each cached page with the entities it depends on, so a product page carries a tag like product:12345, and when something changes you call the revalidation API with that tag and the framework rebuilds just those pages on the next request. The developer experience is clean because invalidation lives in your application code next to the rendering, and the granularity is excellent because tags map to your data model rather than to URLs.
A minimal Shopify to Next.js handler looks like this.
// app/api/revalidate/route.js
import { revalidateTag } from 'next/cache'
export async function POST(req) {
const topic = req.headers.get('x-shopify-topic')
const body = await req.json()
if (topic === 'products/update') {
revalidateTag(`product:${body.id}`)
for (const c of body.collections ?? []) revalidateTag(`collection:${c}`)
}
if (topic === 'inventory_levels/update') {
revalidateTag(`inventory:${body.inventory_item_id}`)
}
return Response.json({ revalidated: true })
}
The catch is in the first line of the handler. This only fires if the webhook arrives, is authentic, and is processed. If Shopify's delivery is delayed, if your endpoint returns a non success status and the retry eventually gives up, or if you deployed a bug in the handler, the tag is never invalidated and the page stays stale forever, or at least until some other event happens to touch the same tag. On demand ISR has no built in floor on staleness. That property, that it is exactly as reliable as your webhook pipeline, is why webhook delivery reliability is not a side concern here but the load bearing dependency, which we treated on its own in the webhook delivery reliability analysis. The framework tie in also means this pattern is cleanest on the framework that owns the cache, which factors into the Hydrogen versus Next.js commerce decision.
Webhook Driven Purge: The CDN Native Version of the Same Bet
Webhook purge moves the same event driven idea down a layer, from the framework cache to the CDN. Instead of regenerating a page, the webhook handler tells the CDN to evict the cached response so the next request misses and refetches. The strategy lives or dies on cache tags, sometimes called surrogate keys. When you cache a page you attach keys describing what it contains, so a product page is tagged with its product id and its collections, and a single purge by key can evict every page that references product 12345, including the product page, the collection pages it appears on, and the homepage module that features it. Without tags you are purging by URL, which means you have to know every URL a product touches, and in a real catalog you do not.
Two failures define this approach. The first is the same missed event problem as on demand ISR, because a purge that is never triggered never happens. The second is specific to purging: the thundering herd. When you evict a very popular page, the next wave of visitors all miss the cache at once and stampede your origin to regenerate it, which is exactly when your origin is least able to cope. The defenses are request coalescing at the edge so only one regeneration runs while others wait, and staggered or soft purges that let the edge serve the old copy for a moment while the new one builds. Which CDN you are on shapes how much of this you get for free, because tag support and coalescing behavior vary widely across the edge platforms, a difference we compared in the Fastly versus Cloudflare versus Vercel edge analysis.
Timed Revalidation: The Reliable, Honest, Blunt One
Timed revalidation sets a TTL on the cache and lets entries expire on their own. The refinement that makes it usable is the HTTP stale-while-revalidate directive, which lets the edge keep serving the expired copy instantly while it refetches a fresh one in the background, so visitors never wait on a cache miss and the page updates a beat later. There is no event, no webhook, no tag, and therefore nothing to miss. The cost is stated plainly in the directive: for the length of the window you choose, the page may be stale, and you have simply decided that is acceptable.
For content that changes on the scale of hours or days, editorial copy, category descriptions, brand pages, this is the right tool and the other two are overkill. For price it is marginal, because a fifteen minute staleness window on a price is a customer seeing one number and being charged another, which is a support ticket at best. For inventory it is usually wrong on its own, because inventory can change many times a minute during a busy period and no TTL short enough to track it is long enough to be worth caching. The honest role of timed revalidation in a commerce stack is not to be the primary strategy for volatile data. It is to be the floor under the event driven strategies, the guarantee that even if every webhook fails, no page can be stale for longer than the window.
The Hybrid That Actually Ships
The production pattern is to stop choosing. Use an event driven strategy, either on demand ISR or webhook purge depending on whether your cache lives in the framework or the CDN, as the primary path, so the common case is that changes propagate within seconds and precisely. Then set a background TTL with stale-while-revalidate as a backstop, long enough that it does not add meaningful origin load, short enough that a missed event self heals in an acceptable time, often somewhere between a few minutes and an hour depending on the data. The event path gives you freshness. The timed path gives you a guarantee that freshness has a worst case, so a dropped webhook degrades to a short delay instead of a permanent lie.
Two design rules make the hybrid hold. First, decide what you refuse to cache at all. Carts, checkout, and any customer specific view should never be edge cached, because their whole value is that they are personal and current, and the performance you would gain is not worth the correctness you would lose. Second, treat inventory as a special case rather than forcing it through the page cache. The highest change rate field in commerce is stock level, and invalidating a page on every inventory tick can generate more purge traffic than the page views it protects. The common answer is to cache the product page shell, which is stable, and read the volatile availability separately, either from the edge or from the client at request time, so the number that changes every few seconds does not drag the whole page's cache with it.
When This Applies To Your Stack
If your storefront is headless and fast, you already have a cache invalidation strategy, whether you designed one or inherited the defaults, and the question is only whether it fails safely. Event driven invalidation, on demand ISR or webhook purge, is what makes the store correct within seconds, and it rests entirely on a webhook pipeline you must treat as production critical. Timed revalidation with stale-while-revalidate is what keeps a missed event from becoming a lasting error. And the two fields that break the naive approach, price and inventory, deserve explicit handling rather than being thrown into the same cache as your marketing copy. Build the layers on purpose and a dropped event costs you minutes. Build one layer and hope, and it costs you a customer charged the wrong price.
If your team is standing up or repairing a headless storefront and wants the caching designed so the site stays fast and still tells the truth about price and stock, Contra Collective builds headless commerce and platform migrations where invalidation is architected against your real change rates, not left to framework defaults. Speed is easy to buy. Correctness under change is the part that takes design.
FAQ
What is cache invalidation in a headless commerce storefront? It is the mechanism that updates or evicts cached pages when the underlying commerce data changes, so a product page stops showing an old price or a stale in stock badge. Because a headless storefront caches pages for speed, invalidation is what decides how quickly the cache reflects reality after a change in the source system such as Shopify.
What is the difference between on demand ISR and webhook purge? Both are event driven and both are triggered by a webhook, but they act at different layers. On demand ISR regenerates specific pages inside the framework cache by calling a revalidation API with a tag. Webhook purge evicts entries from the CDN so the next request refetches. ISR suits framework owned caches like Next.js, purge suits CDN centric setups with cache tag support.
Is stale-while-revalidate enough for price and inventory? Usually not on its own. It guarantees a page is refreshed within a fixed window but allows staleness for the length of that window, which is risky for price, where a customer can see one number and be charged another, and poor for inventory, which changes too often to track with any reasonable TTL. It works best as a backstop under event driven invalidation, not as the primary strategy for volatile data.
Why do missed webhooks matter so much for caching? Because event driven invalidation only updates a page when its event arrives. If a webhook is delayed, unauthenticated, or fails processing without a successful retry, the page is never invalidated and stays stale indefinitely. That is why a timed revalidation floor is important and why the reliability of the webhook pipeline is a core dependency of any event driven cache strategy.
Should inventory changes invalidate the whole product page? Often no. Inventory is the highest change rate field in commerce, and invalidating the full page on every stock update can create more purge load than the page saves. A common pattern is to cache the stable product page shell and read availability separately, from the edge or the client at request time, so the fast changing number does not drag the whole page cache with it.
More from the lab.
Segment vs RudderStack vs mParticle: Customer Data Platform for Headless Commerce (2026)
A customer data platform is the least visible and most load-bearing piece of a headless commerce stack. It is the layer that captures every event from the storefront, the mobile app, and the backend, resolves those events into a single view of the customer, and fans them out to analytics, email, ads, and the warehouse. Because it sits in the middle of everything, the CDP choice quietly decides three things that are expensive to change later: whether you own your customer data or rent access to it, how much of your compliance and consent surface lives inside a vendor versus your own infrastructure, and how your bill scales as event volume grows, which in commerce it always does. Segment, RudderStack, and mParticle are the three platforms most enterprise commerce teams shortlist, and they embody genuinely different philosophies rather than being feature-for-feature clones. Segment is the managed incumbent optimized for time to value. RudderStack is the warehouse-first, self-hostable challenger built for teams that want to own the pipeline. mParticle is the mobile-heavy enterprise option with the deepest identity and audience tooling. For a headless architecture, where the storefront is decoupled and events originate from several surfaces at once, the differences in how each handles server-side collection, identity resolution, and pricing at volume are what separate a clean integration from a costly one. This post lays out those differences and the decision framework that follows from them.
Multi Currency and Cross Border Pricing in Headless Commerce: Duties, Rounding, and the Presentment Problem (2026)
Selling internationally on a packaged storefront is mostly a settings screen: turn on the currencies, let the platform convert, and the theme shows the right number. Go headless and the convenience disappears, because now your own frontend is responsible for asking the API for a price in the customer's currency, displaying it with the correct rounding, carrying that currency all the way through the cart and into checkout, and reconciling what the customer paid against what the store settles in. Each of those steps has a way to go wrong that a themed store never exposed you to, and the most common one is subtle: the storefront requests a presentment currency and the API quietly returns the amount in the store's base currency anyway, so the customer sees a euro sign in front of a dollar number. Add duties and import taxes on a cross border order and the surface expands again, because now the total the customer sees at checkout has to include or exclude a duty depending on whether you sell delivered duty paid or delivered duty unpaid, and getting that wrong means either a surprised customer or a margin you did not plan to give away. This post is about the architecture that keeps a headless international store honest: which system owns the converted price, how presentment currency actually flows through the Storefront API, why rounding is a real rule and not a rounding error, and how duties change the checkout total.
Migrating from Salesforce Commerce Cloud to Headless Shopify Plus: A Replatforming Playbook (2026)
The decision to leave Salesforce Commerce Cloud is usually made on cost and velocity, and by the time it reaches engineering it has hardened into a deadline. That is where replatforming projects go wrong, because moving from SFCC to a headless Shopify Plus stack is not a migration in the copy the data and flip the switch sense; it is a rebuild of the parts of your commerce logic that lived inside SFCC cartridges and pipelines, wrapped around a data migration that is the easy part by comparison. SFCC gave you a monolith where the storefront, the business logic, and the platform were fused, and headless Shopify Plus deliberately unfuses them: Shopify becomes the commerce engine behind an API, and the storefront becomes your own application. Everything that made SFCC feel complete, the cartridge ecosystem, the pipeline customizations, the server side rendering baked in, becomes something you now own explicitly. This playbook walks the migration in the order that actually de-risks it: what maps cleanly from the SFCC data model to Shopify, what has to be rebuilt rather than ported, how to protect the SEO equity that a careless cutover destroys, and the sequencing that lets you move without a big bang launch. The projects that fail treat this as a data problem. The ones that succeed treat it as a rebuild with a data migration attached.