TL;DR: The back/forward cache (bfcache) keeps a full snapshot of a page in memory so the back button restores it instantly, with no network trip. Chrome data shows 1 in 5 mobile navigations are back or forward, and on a Shopify store most of those are shoppers bouncing between collection and product pages. Many stores quietly lose bfcache to an old unload listener in a theme or app. This guide shows how to test Shopify bfcache support, find what blocks it, and fix it.
In this guide
- What the back/forward cache does on a Shopify store
- Why instant back navigation matters for conversion
- What breaks bfcache on Shopify
- How to test Shopify bfcache support
- A fix plan for Shopify themes and apps
- How to measure your bfcache hit rate
- Frequently asked questions
Every Shopify brand obsesses over the first page load. Fewer look at the second, third, and fourth. Yet a shopper's session is mostly repeat navigation: open a collection, tap a product, tap back, tap another product, tap back again. On mobile, where browsing is thumb driven, that loop is the core of product discovery. Shopify bfcache support decides whether each of those back taps feels instant or feels like a fresh page load. When it works, the collection page reappears in the exact scroll position, with filters applied and images already painted. When it fails, the browser rebuilds the page from scratch, re-runs every app script, and often dumps the shopper back at the top of a 200 product grid.
The good news: bfcache is a browser feature. You do not buy it or install it. You only need to stop blocking it.
What the back/forward cache does on a Shopify store
The back/forward cache is an optimization built into Chrome, Safari, Firefox, and Edge. When a shopper navigates away from a page, the browser does not destroy it right away. It pauses the page's JavaScript and keeps the entire page, including the DOM and the JavaScript heap, in memory. If the shopper presses back soon after, the browser simply unfreezes that snapshot and shows it.
This is different from the HTTP cache. The HTTP cache stores individual responses (HTML, CSS, images, scripts), and the browser still has to parse, lay out, and execute everything again. The bfcache stores the finished, running page. That is why a bfcache restore is faster than even the best optimized repeat page load.
For a Shopify storefront, that means:
- Collection pages come back with scroll position, loaded images, and active filters intact.
- Third party apps (reviews, upsells, chat widgets, analytics) do not re-initialize, so there is no fresh wave of main thread work.
- No request hits Shopify's servers or your CDN, which also saves the shopper's mobile data.
- Largest Contentful Paint for the restored view is effectively the time to paint one frame.
Browsers still apply limits. A page only stays in the bfcache for a short window, the browser can evict it to save memory, and certain page behaviors make it ineligible. Those behaviors are where most Shopify stores lose out.
Why instant back navigation matters for conversion
According to Chrome usage data published by the Chrome team, 1 in 10 navigations on desktop and 1 in 5 on mobile are back or forward navigations. Ecommerce is likely above that average, because comparison shopping is built on the back button.
Now picture that loop on a store where bfcache fails. Each return to the collection triggers a full page load. On a mid range Android phone over cellular, that can mean one to three seconds of blank or shifting content, a burst of app scripts competing for the main thread, and a scroll position that may or may not be restored. Multiply that by eight product views and you have added many seconds of friction to a single session. We covered how small delays compound in our analysis of Shopify speed and conversion data: shoppers do not experience your store as one page load; they experience the sum of every wait.
There is also a Core Web Vitals angle. The Chrome User Experience Report (CrUX) treats bfcache restores as separate page visits, and those restores are nearly instant. Stores that enable bfcache add a large number of very fast experiences to their field data. Stores that block it replace those instant visits with regular page loads that pull the 75th percentile the wrong way. If you have been trying to move your field LCP and Interaction to Next Paint scores, bfcache is one of the few levers that improves real user data without changing a single asset.
Finally, bfcache pairs well with forward looking speed techniques. Speculation rules make the next page instant by prerendering it; bfcache makes the previous page instant by keeping it. Together they cover both directions of the browsing loop.
What breaks bfcache on Shopify
Browsers decide eligibility page by page. On Shopify, the blockers almost never come from Shopify's core platform. They come from theme code and, far more often, from apps and tags injected into the storefront. These are the usual suspects.
The unload event listener
This is the number one bfcache killer. The unload event predates bfcache, and a lot of older code assumes a page is gone once unload fires. On desktop, Chrome and Firefox make any page with an unload listener ineligible for bfcache. Mobile Chrome and Safari are more lenient, but the listener still makes behavior unpredictable. The official guidance from the Chrome team is blunt: never use the unload event. Use pagehide instead, which fires in every case unload does and also fires when a page enters the bfcache.
On Shopify stores, unload listeners usually hide in older analytics snippets, session recording tools, legacy affiliate or attribution pixels, and theme code written years ago. A single app is enough to disqualify every page it loads on.
Cache-Control: no-store on the page
Historically, browsers refused to put a page in bfcache when the HTML response carried Cache-Control: no-store. Chrome has since shipped a change that allows bfcache for many no-store pages when it is safe, for example when cookies have not changed since the page was stored. Other browsers may still treat no-store as a blocker. If your storefront sits behind a custom proxy or headless frontend, check that you are not setting no-store on public product and collection pages that do not need it.
Open connections and in flight requests
Pages holding an open IndexedDB connection, an in progress fetch() or XMLHttpRequest, or a WebRTC connection can be excluded by some browsers. Live chat widgets, real time inventory counters, and "someone just bought this" social proof popups are common sources on ecommerce sites. Recent Chrome versions no longer block on open WebSockets, but other browsers still may.
Cart and checkout state
This one is not a blocker; it is a risk to plan for. If a shopper adds an item to the cart, then presses back to a page with a stale cart count in the header, the restored page will show old data. The fix is not to disable bfcache. It is to refresh cart state when the page is restored, which we cover in the fix plan below.
How to test Shopify bfcache support
You can check any storefront page in under a minute with Chrome DevTools:
- Open a collection page on your live store in Chrome (test the live theme, not only the theme editor preview).
- Open DevTools and go to Application, then Back/forward cache.
- Click Test back/forward cache. DevTools navigates away and back, then reports whether the page was restored.
- If it fails, the panel lists the reasons and marks the ones you can fix as Actionable. The DevTools documentation explains each reason.
Repeat the test on your homepage, a collection, a product page, and the cart page. Results often differ by template because apps load conditionally. Lighthouse also includes a bfcache audit, but remember the distinction we laid out in lab data vs field data: a lab test tells you whether your page can be cached, not how often real shoppers get a restore.
To find which script added an unload listener, run getEventListeners(window) in the DevTools console on the page. Expand the unload entry and follow the source link to the file. On Shopify, the file path usually points to an app's CDN domain, a theme asset, or a snippet loaded through the Customer Events or Web Pixels system.
A fix plan for Shopify themes and apps
Work through these steps in order. Most stores get the majority of the benefit from the first two.
1. Replace unload with pagehide in theme code
Search your theme files for unload (in Liquid, JavaScript assets, and inline scripts). Any code that sends a final analytics beacon or saves state on exit should listen to pagehide instead. The change is usually one word. Pair it with navigator.sendBeacon() for reliable delivery of exit data.
2. Audit and pressure apps that add unload listeners
You cannot edit app code directly, but you can report it. Contact the vendor with the DevTools output and ask for a pagehide based version. If the app offers little value, this is a good moment to remove it; our guide to third party apps and page speed walks through how to score each app on cost and value. Once your own code is clean, consider sending the header Permissions-Policy: unload=() through your edge layer. It prevents any script, including future apps and browser extensions, from registering unload handlers on your pages.
3. Refresh cart and inventory on restore
Listen for the pageshow event and check its persisted property. When it is true, the page came from bfcache, so re-fetch /cart.js and update the header cart count and any drawer contents. Do the same for low stock badges or pricing that can change quickly. This keeps the instant restore while guaranteeing accurate data. Pages showing account details should re-check the session and reload if the shopper logged out.
4. Close connections on pagehide
If your theme or a custom widget opens IndexedDB, holds a long running fetch, or keeps a live connection, close it on pagehide and reopen it on pageshow. Ask chat and social proof vendors whether their widgets do this already.
5. Keep public pages off no-store
If you run a headless storefront or a custom proxy, serve product, collection, and content pages with Cache-Control: no-cache or max-age=0 when you need freshness. Those directives keep bfcache eligibility intact. Reserve no-store for account and truly sensitive pages.
How to measure your bfcache hit rate
Fixing blockers is only half the job. You also want proof in real user data, and you want your analytics to count restored pages correctly.
Count restores as page views. Many analytics setups do not record a page view when a page comes back from bfcache, because no new page load happens. As your hit rate rises, reported page views can dip even though shoppers are browsing more. Add a pageshow listener that sends a page view when event.persisted is true, and tag it with a navigation type such as back_forward_cache.
Calculate your hit ratio. Compare the count of back_forward_cache restores to the count of regular back_forward navigations. A 100% ratio is not realistic, since browsers evict pages for memory and some back navigations happen after a tab restart, but pages far below the rest of your site point to a blocker on that template.
Diagnose failures in the field. Chrome's NotRestoredReasons API reports why a back navigation did not use bfcache for real visitors. Logging it for a week reveals which apps block restores on the devices your customers actually use.
Watch CrUX navigation types. CrUX now publishes navigation type data, so you can see the share of your origin's visits served from bfcache without any custom code. Track it alongside LCP, INP, and CLS. A rising bfcache share and improving field vitals together are the clearest signal that the fix is working.
Frequently asked questions
Does Shopify support the back/forward cache by default?
Shopify storefront pages can be bfcache eligible, since bfcache is handled by the browser, not the platform. Whether a given store actually gets restores depends on its theme code and installed apps. A single app with an unload listener can make pages ineligible on desktop Chrome and Firefox.
Will bfcache show shoppers an outdated cart?
It can, if you do nothing. A restored page shows the state it had when the shopper left. Add a pageshow listener that re-fetches the cart when event.persisted is true, and the header count and cart drawer stay accurate while the page still restores instantly.
Why did my Shopify page views drop after fixing bfcache?
Restored pages do not trigger a new page load, so analytics tools that only count loads miss them. Shoppers are not browsing less. Send a page view on pageshow when event.persisted is true to count bfcache restores, and compare sessions and conversion rate rather than raw page views.
Does the back/forward cache help Core Web Vitals and SEO?
Yes, indirectly. CrUX counts bfcache restores as page visits with near instant loading, which improves the field data Google uses for Core Web Vitals. It does not change how Googlebot crawls your store, but better field vitals support page experience signals.
How do I find which Shopify app is blocking bfcache?
Run the Back/forward cache test in Chrome DevTools under Application, then run getEventListeners(window) in the console and inspect the unload entries. The source file path usually names the app's domain. For field data, log the NotRestoredReasons API to see blockers on real devices.
Make every tap feel instant. Fixing bfcache is one of the cheapest speed wins available to a Shopify brand: no redesign, no new assets, often just a one word code change and a conversation with an app vendor. It pays off on the navigation shoppers use most while comparing products. Nostra's Edge Delivery Engine handles the other side of the loop, serving every first visit and next page from the edge so your storefront is fast in both directions. Want to see how your store performs today? Book a demo and our team will walk through your field data, bfcache blockers, and the fastest path to a quicker store.