Cache Invalidation in Headless: How Next.js Learns When Bitrix Updates a Product
The complaint came on day three after the headless launch. A store manager updated a price in Bitrix at 10:47. A customer added the product to their cart at 11:20 — at the old price. The Next.js page was cached for one hour.
Everything worked exactly as designed. The business wasn't happy about it.
We spent three hours not debugging — but explaining that this wasn't a bug. It was a decision we'd made during architecture planning. We just hadn't told the client about it.
The problem: stale data in headless isn't a bug
In a headless architecture, Bitrix is a REST API backend and Next.js is a frontend that fetches data over HTTP and caches it independently. When Bitrix updates a product, Next.js has no automatic way to know. This is structural, not a flaw — and it's the first thing to explain to a client before launch.
In a monolithic Bitrix setup, this issue doesn't exist. The request hits the template, the template hits the database, the data is current. Caching lives on the same machine with the same database access, and Bitrix core handles invalidation. Headless breaks that cycle entirely.
On a project with 28,000 SKUs, we had up to 300 product updates per day: prices, stock levels, descriptions. Every update in Bitrix was a potential divergence from what users saw on the frontend. The question wasn't whether divergence would happen, but how long it would last and what the business impact would be.
Three freshness strategies
| Strategy | Data freshness | TTFB (product page) | Infrastructure cost | |----------|---------------|---------------------|---------------------| | SSR | Always current | 1.1–1.4 s | High (Bitrix API on every request) | | ISR with TTL | Lags by TTL (e.g. 1 hour) | ~180 ms | Low | | On-demand revalidation | 1–2 min after Bitrix update | ~180 ms | Low + queue overhead |
The first option is SSR (server-side rendering). Next.js generates each page on every request. Data is always current — no staleness, just a short CDN TTL. The cost: TTFB. On product pages, we measured 1.1–1.4 seconds TTFB with SSR, versus 180ms with ISR. At 28,000 SKUs and meaningful traffic, that puts steady load on the Bitrix API and inflates rendering costs.
The second is ISR with a TTL. Next.js builds a static page and regenerates it in the background every N seconds, triggered by the next request after the TTL expires. Data can lag behind by up to one TTL. We set revalidate: 3600 — one hour — and that's exactly what produced the price situation. Could we drop it to 60 seconds? Sure, but then Next.js is rebuilding 28,000 pages 1,440 times a day. That's real infrastructure and API load.
The third is on-demand revalidation. Next.js invalidates a specific page or tag on external request, via revalidatePath() or revalidateTag(). Bitrix fires a webhook when a product changes. Next.js receives it at /api/revalidate and purges only the affected pages.
This is closest to the "right" answer. It's also the most fragile.
Why Bitrix webhooks aren't reliable enough on their own
Bitrix supports webhooks for some events: price changes, stock updates, description edits. Not all events. Bulk imports via 1C exchange don't reliably trigger per-item webhooks — this depends on the Bitrix version and the exchange configuration.
Then there's delivery. If Next.js is momentarily unavailable when Bitrix fires a webhook, there's no guaranteed retry. The webhook is gone, the invalidation never happens, the data stays stale — with no signal that anything went wrong.
We solved this with a queue. Bitrix writes change events to a database table. A cron job reads that table every minute and calls /api/revalidate. It's not elegant. It works.
Our approach: ISR + on-demand revalidation
The setup that stuck:
- All catalog and product pages: ISR with TTL 3600 as a fallback.
- On-demand revalidation via the event queue — for prices and stock levels (the fields that matter to the business).
- SSR — only for cart and account pages where data must always be current.
The /api/revalidate endpoint accepts a POST with an auth token and a list of slugs or tags. Inside: revalidateTag() for groups, revalidatePath() for specific URLs. The tricky part is mapping product IDs to all the pages that show them, including category pages with product tiles that display prices.
When Bitrix can't send a webhook
A few cases we hit in production:
Bulk imports via 1C: no per-item webhooks. After the exchange finishes, a script pulls the changed items from the exchange log and queues them for invalidation.
Admin edits that write directly to the database: some operations bypass the Bitrix API layer entirely and don't trigger any event. We handle those with a periodic diff every 15 minutes on key fields.
SEO field changes (meta title, description): these don't affect purchase flow and aren't time-sensitive. TTL 3600 is fine for them.
Some of this is messy. Pragmatic, but messy.
The cost of freshness: LCP and infrastructure
Switching the full catalog to SSR would push TTFB from 180ms to 1.1–1.4 seconds. LCP takes roughly an 800–900ms hit. That's a direct effect on Core Web Vitals — and indirectly on organic rankings.
On-demand revalidation with a queue adds about 1–2 minutes between a Bitrix update and the page refresh on the frontend. For most products, that's acceptable. For a flash sale with a countdown timer, it isn't — those pages run on SSR.
We also stress-tested: 50 simultaneous revalidation requests during active traffic. Bitrix held. But it's something to keep an eye on as catalog size grows.
When SSR beats ISR
ISR makes sense for content that changes rarely and doesn't need to be exact: editorial content, category pages, static landing pages. For prices in an e-commerce store, it's a harder call.
If you have legal requirements around price accuracy — consumer protection rules, price parity agreements — ISR with a 1-hour TTL is a liability. Before you pick a caching strategy, ask the client directly: what happens if a customer sees a stale price? A refund dispute? A regulatory issue? Just an apology email? That answer determines your architecture more than any performance benchmark does.
Cache invalidation in headless isn't a technical problem. It's a product decision you have to make before launch. We made it on day three. Next time, it's the first question on the scoping call.
Related: