WooCommerce Pageviews Are Quietly Under-Counted: The Back/Forward Cache Problem
TL;DR:
Cache-Control: no-store
pageshow
In this article:
- What the back/forward cache actually does
- Why GA4 misses bfcache page_view events
- The 2025 change that widened the WooCommerce gap
- How to diagnose bfcache under-counting on your store
- How to fix it: pageshow listener and server-side alternatives
- What it means for Smart Bidding and ROAS
- Key Takeaways
- FAQ
What the back/forward cache actually does
bfcache is a memory snapshot mechanism that lets browsers restore pages instantly on back/forward navigation — but that frozen state means your tracking JS doesn’t re-run.
When a user navigates away from a page and hits the back button, the browser has two options. It can reload the page from scratch — making a new network request, parsing HTML, executing scripts, firing events. Or it can restore a frozen memory snapshot of the page as it was when the user left. That second option is the back/forward cache.
bfcache is fast — and it’s silent. The page appears instantly. The user never sees a load indicator. But the JavaScript execution context was frozen when they left, and it stays frozen on the way back. load and DOMContentLoaded do not fire. Your gtag calls don’t run. Your Meta Pixel PageView event doesn’t fire. From your analytics platform’s perspective, the back-button return never happened.
The one event that does fire is pageshow — and it carries a persisted property set to true when the page was restored from bfcache. That’s the hook you need to re-fire tracking manually. Most WooCommerce stores aren’t using it.
About 1 in 5 mobile navigations and 1 in 10 desktop navigations are back/forward taps — and every single bfcache restore silently skips your page_view and pixel events, according to web.dev (Google).
Why GA4 misses bfcache page_view events
GA4’s measurement model assumes every page view fires a fresh JavaScript execution — an assumption bfcache breaks by design.
GA4’s page_view event is triggered by gtag.js or Google Tag Manager during script execution on page load. When bfcache restores a page, that execution cycle is skipped entirely. The scripts that already ran when the user first loaded the page are still in memory, but they don’t re-run.
The result: GA4 records one page_view on the first visit and zero on any subsequent back-button returns to that page. If a shopper browses your product catalogue — going forward into a product page, back to the listing, forward again — you’re potentially missing one page_view event for every back-button tap in that session.
The same problem applies to Meta Pixel, TikTok Pixel, and any other client-side event that relies on script execution at page load. They all see a frozen page and report nothing.
You may be interested in: The GA4 reporting identity setting that silently rewrites your conversion numbers
bfcache restores skip
loadandDOMContentLoadedentirely — so any tracking code that depends on those events, including GA4’s default page_view, simply doesn’t run on back-button navigation.
The 2025 change that widened the WooCommerce gap
Chrome’s March–April 2025 bfcache-for-no-store rollout removed the main mechanism that was protecting many WooCommerce stores from this problem.
For years, the most common bfcache blocker was the Cache-Control: no-store header. Pages that sent this header were ineligible for bfcache. Many WooCommerce pages — particularly checkout, cart, and account pages — have traditionally used no-store to prevent sensitive data from being cached.
That exclusion is gone. Chrome completed its rollout of bfcache support for no-store pages to 100% of users in March–April 2025. According to Chrome for Developers, Cache-Control: no-store was previously blocking bfcache on roughly 17% of mobile and 7% of desktop history navigations. That’s a significant proportion of back/forward traffic that was previously tracked correctly — and now isn’t.
WordPress 6.9, released December 2025, shipped instant back/forward history transitions as well, further expanding which pages are bfcache-eligible. WooCommerce stores on WordPress 6.9+ running Chrome are now more exposed to this data loss than at any point previously.
| Data loss type | Mechanism | Direction | Smart Bidding impact |
|---|---|---|---|
| bfcache under-counting | Frozen JS skips page_view on back-button | Under-reports pageviews | Underbids on high-intent return visits |
| Speculative loading (prerender) | Page loaded before user clicks; event fires on render | Over-reports pageviews | Overbids on intent signals that didn’t convert |
| Cache-Control: no-store (pre-2025) | Blocked bfcache; tracking fired normally | Correct count | Accurate signals (no longer reliable) |
| Ad blocker interference | Blocks gtag.js or Pixel scripts | Under-reports events | Compresses all signal types uniformly |
How to diagnose bfcache under-counting on your store
Chrome DevTools has a built-in bfcache inspector that shows exactly which pages are eligible and which blockers still apply.
Open Chrome DevTools on your WooCommerce store, navigate to the Application panel, and look for the Back/Forward Cache section. Test a page by navigating away and returning with the back button. DevTools will tell you whether the page was served from bfcache and, if not, which condition blocked it.
Run this test on your highest-traffic pages first. Product pages, category listings, and the shop page are the most likely candidates — these are the pages shoppers navigate back to most often during a session. Cart and checkout pages are worth testing post-2025 Chrome updates.
In GA4, cross-reference your pageview counts against your server logs or a server-side analytics source. If your server records significantly more page requests for certain URL paths than GA4 shows, bfcache is a likely contributor — particularly if the gap correlates with mobile traffic, where back/forward navigation rates are highest.
The Chrome UX Report (CrUX) is another signal. CrUX counts bfcache restores as separate visits, so a visible divergence between CrUX traffic data and your GA4 dashboard can indicate bfcache restores are happening but going untracked in GA4.
How to fix it: pageshow listener and server-side alternatives
The client-side fix is a single event listener; the structural fix is moving tracking off the client entirely.
The standard client-side fix is a pageshow event listener that checks whether the page was restored from bfcache and, if so, re-fires your tracking calls manually:
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
gtag('event', 'page_view', {
page_location: window.location.href,
page_title: document.title
});
if (typeof fbq !== 'undefined') {
fbq('track', 'PageView');
}
}
});
This can be added via Google Tag Manager as a Custom HTML tag triggered on the Page Show event — GTM has a built-in trigger for this. You can also add it directly in your theme or a site-specific plugin.
You may be interested in: Why server-side tracking is still essential even after Google cancelled Privacy Sandbox
The deeper fix is server-side tracking. A server-side pipeline receives navigation signals based on actual HTTP requests and user sessions — not on client-side JS execution. It doesn’t freeze. It doesn’t care whether the browser restored a page from bfcache or loaded it fresh. Every navigation that reaches your server gets recorded, because the recording happens on your infrastructure, not in the user’s browser.
This is particularly important for conversion events. A server-side purchase event fires when your WooCommerce order is created — not when a script runs in a browser tab that may be frozen, throttled, or blocked. The reliability difference is meaningful at scale.
What it means for Smart Bidding and ROAS
Incomplete page_view signals mean Smart Bidding models on incomplete session data — and the bfcache problem compounds with speculative loading in opposite directions.
Smart Bidding uses GA4 conversion signals to build its audience and intent models. When bfcache silently drops page_view events for back-button navigations, Smart Bidding sees a compressed picture of user behaviour — shorter apparent session paths, fewer touchpoints before conversion. It may underbid on users who are actually high-intent return visitors, because those returns aren’t visible.
Here’s the twist: speculative loading (prerender) does the opposite. When Chrome pre-renders a page before a user clicks, the page_view event fires on render — before the user has actually engaged. GA4 over-counts. Smart Bidding receives inflated signals. The two mechanisms can run simultaneously on the same store, pushing ROAS models in opposite directions.
The combined effect is noisy, inconsistent signal data — and it’s particularly hard to diagnose because the errors are structural and invisible, not the kind that surface as obvious anomalies in your GA4 dashboard.
Server-side tracking anchors your conversion data to server events — order creation, form submission, payment confirmation — that don’t depend on client-side JS timing. That’s the foundation that makes Smart Bidding signals trustworthy regardless of what the browser’s caching layer is doing.
Key Takeaways
- bfcache freezes JS on back-button navigation: When Chrome restores a page from the back/forward cache, your GA4 page_view and pixel events don’t re-fire — about 1 in 5 mobile and 1 in 10 desktop navigations are affected.
- The 2025 Chrome rollout expanded the problem: Chrome removed the
Cache-Control: no-storebfcache blocker in March–April 2025, making more WooCommerce pages eligible and widening the under-count gap. - The client-side fix is a pageshow listener: Check
event.persisted === trueand re-fire your GA4 and Pixel tracking calls manually on bfcache restores. - Server-side tracking is the structural solution: A server-side pipeline records navigations based on actual HTTP requests — unaffected by bfcache, JS freezing, or ad blockers.
- Smart Bidding is affected: Missing page_view signals compress apparent session paths and can cause underbidding on high-intent return visitors — compounding with speculative loading over-counting from the other direction.
When users hit the back button, Chrome restores the page from the back/forward cache (bfcache) — a frozen memory snapshot. The page JS never re-runs, so GA4’s page_view event never fires. Your server already served the page on the first load and logs the request, but GA4 misses the back-button return entirely.
Not by default. bfcache restores freeze the JavaScript execution context, which means gtag calls and Meta Pixel fire() functions don’t run. You need a pageshow event listener that checks event.persisted === true and manually re-fires your tracking calls on cached restores.
Add a pageshow listener: window.addEventListener(‘pageshow’, (e) => { if (e.persisted) { gtag(‘event’, ‘page_view’, { page_location: location.href }); } }); For production stores, pair this with a server-side tracking layer so navigation events are recorded regardless of client-side JS state.
The bfcache mechanism has existed for years, but Chrome’s March–April 2025 rollout of bfcache-for-no-store pages dramatically expanded which WooCommerce pages are affected. Pages with Cache-Control: no-store headers were previously excluded. Now they’re eligible, meaning the under-count gap widened significantly in 2025.
Smart Bidding uses GA4 conversion signals to optimise ad spend. If bfcache is silently dropping page_view events, Smart Bidding sees fewer signals and may underbid on high-intent return visitors. Meanwhile, speculative loading (prerender) can over-count pageviews, making ROAS signals unreliable in both directions simultaneously.
References
If your WooCommerce store is running client-side-only tracking, bfcache restores are silently skipping your events on every back-button tap. The Transmute Engine™ routes your conversion data through a server-side pipeline that records every navigation regardless of browser caching state — so your GA4 and Meta Pixel signals reflect what actually happened. See how it works at seresa.io.