If you’ve never heard of it, the back/forward cache (bfcache
) is a browser optimization introduced in 2019 that is available in all major browsers, including Chrome (or Chromium-based browsers, version 96 and above), Firefox, and Safari. It allows for instant navigation when moving back and forth between pages. This feature significantly improves the browsing experience, especially for users on slower connections or with less powerful devices.
The way it works is that when you navigate to a new page, the browser, instead of immediately destroying the current page, delays it and pauses JavaScript execution. This allows the page to load instantly if the user clicks the back button, as the page is restored immediately from memory.
This is beneficial in almost every imaginable way, but when it comes to measuring our site, it poses a challenge. Since JavaScript execution is paused and the page is restored when the user navigates back ( or forward ), our scripts won’t fire again, which means our tags won’t trigger or re-evaluate. Meaning that for example the page_view
won’t fire. Oh boy that’s not good, let’s see what happens on a site when the bfcache
is doing it’s thing.
I think this is really important because it means you could be missing crucial page_view tracking on your site, or certain pixels might not be firing as expected. This could lead to gaps in your data collection, making it difficult to accurately measure user interactions and performance. Without proper tracking in place, you may miss key insights, such as user behavior on different pages or the effectiveness of your marketing campaigns. Ensuring that your ‘page_view‘ events are consistently fired across all page types—whether regular page loads, virtual pageviews, SPA sites, or bfcache-restored pages—will help guarantee that you’re capturing all the necessary data for effective analysis and optimization.
As shown below, users navigating through the site won’t trigger any page_view events (or any other events). Meaning that I may be missing the measure of my blog content being viewed :(.
There’s some ways of checking if this is happening to us, easiest way it just checking the console, because, at least Chrome, will let us know when the page is being restored from the bfcache
How BFCache Works
Let’s take a look at how the cache works—it’s a bit different from other caching systems we’re typically used to. The bfcache
is a full snapshot of the entire page, including the JS Heap and gets fully restored on user navigation.
You might wonder what happens to the code running on the site, such as promises or setTimeouts. It’s important to know that these will also be paused, along with most other tasks in queues.
It’s also important to keep in mind that iframes contained within the current page are not eligible for the bfcache
.
Testing BFCache Behaviour
We can also use Developer Tools to force this behavior by going to the Application tab, then navigating to Background Services > Back/forward Cache.
I want my pages to be fully tracked
We need to understand that the Page Lifecycle API internally tracks the current page state, allowing us to know when a page is being frozen (Freeze Event
) or resumed (Resume Event
), as shown in the Page Lifecycle API State Flow below:
These events are emitted to the document, so we can listen to them by adding a simple listener, as shown in the code snippets below.
document.addEventListener('freeze', (event) => {
});
document.addEventListener('resume', (event) => {
});
Also Since Chrome 68 according to the docs, we can even know if the current page was discarded from memory while being hidden checking the wasDiscarded switch for the current document.
if (document.wasDiscarded) {
}
Tracking my pages when they’re restored from bfcache
Luckily for us, the pageshow
event from the Page Lifecycle API that will trigger when a page is loaded, either for the first time or when it is restored from the back/forward cache (bfcache
).
Key points about pageshow event
- It is triggered when a page is shown, which can occur when navigating to a page for the first time or when returning to a cached page.
- The event includes a persisted property that indicates whether the page was restored from the bfcache (back/forward cache). If persisted is true, the page was restored from the bfcache.
Based on this, we can easily add a piece of code to monitor if the page is being restored from the bfcache
. For example, we can push a page_view
event to our dataLayer or trigger a gtag
page_view event directly. Of course, you’ll be able to customize this for your specific needs.
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
window.dataLayer.push({
'event': 'page_view',
'pv_origin: 'from_bfcache'
})
}
});
Pro Tip: page_view event
This is personal advice: even if you don’t have an SPA site or don’t worry about the bfcache
, it’s still a great idea to set up your system to push a ‘page_view’ event every time. Doing so will not only help you establish a common and shared logic for handling regular page loads, virtual pageviews, SPA sites, and bfcache-restored pages, but it will also allow you to avoid race conditions (e.g., by not relying on triggers like ‘All Pages’, DOM Ready, or CMP callbacks). The key is that every time a ‘page_view’ event is fired, it should contain all the necessary data in the dataLayer allow you to have a starndard and safe way to know when triggers things.