What happens when the customer presses the Back button?

What happens when the customer presses the Back button?

Bfcache is one of the highest-impact performance optimizations available on the web, yet it still receives far less attention than Core Web Vitals or Lighthouse scores.

Most teams focus obsessively on first page load. First impressions matter, and there’s a whole industry of tooling built around measuring them. But once users start browsing, they rarely follow a clean linear path. A typical e-commerce journey looks more like this:

---
config:
  layout: elk
---
flowchart LR

subgraph Col1
direction TB
A["Open category page"]
B["View product"]
C["← Back to results"]
A --> B --> C
end

subgraph Col2
direction TB
D["View another product"]
E["← Back to results"]
D --> E
end

subgraph Col3
direction TB
F["Apply filters"]
G["View filtered product"]
H["← Back to compare"]
F --> G --> H
end

C -. Continue browsing .-> D
E -. Refine search .-> F

classDef action fill:#EEF2FF,stroke:#6366F1,stroke-width:2px,color:#111827;
classDef navigation fill:#ECFDF5,stroke:#10B981,stroke-width:2px,color:#111827;
classDef filter fill:#FFF7ED,stroke:#F97316,stroke-width:2px,color:#111827;

class A,B,D,G action
class C,E,H navigation
class F filter

style Col1 fill:none,stroke:#D1D5DB,stroke-width:1px
style Col2 fill:none,stroke:#D1D5DB,stroke-width:1px
style Col3 fill:none,stroke:#D1D5DB,stroke-width:1px

The Back button is one of the most-used controls on the web, particularly on mobile. Chrome data shows 1 in 10 desktop navigations and 1 in 5 mobile navigations are back or forward. Yet many websites treat every Back navigation as a completely new page visit. The page gets rebuilt, JavaScript runs again, network requests fire, components reinitialize. The user experiences another loading delay. From a technical perspective, everything works correctly. From a user’s perspective, it feels like an unnecessary reload.

What bfcache actually does

When a user navigates away from a page, the browser can preserve the page’s state in memory, including scroll position, form inputs, open UI elements, and JavaScript state. When the user presses Back or Forward, instead of rebuilding the page from scratch, the browser restores it exactly as it was.

The page is typically restored almost instantly. Scroll position intact, filter selections intact, everything the user was viewing remains exactly where they left it.

For the user, it feels less like loading a website and more like switching between screens in a native app.

This is fundamentally different from the HTTP cache. The HTTP cache stores responses for individual network requests, while bfcache preserves the entire page execution state, including the JavaScript heap. Even a perfectly HTTP-cached page still has to re-parse HTML, re-execute JavaScript, and re-render. Bfcache skips all of that entirely.

Taking advantage of bfcache is not automatic. Certain browser APIs, response headers, and page lifecycle behaviors can make a page ineligible. Historically, Magento storefronts were blocked by Cache-Control: no-store, while many applications accidentally break restored state by assuming every page visit starts from a fresh load.

Why this matters more than another Lighthouse point

Performance teams often chase synthetic metrics because they’re measurable, comparable, and tied to rankings. But real business impact comes from reducing friction in actual user journeys.

Chrome DevTools includes a Back/Forward Cache diagnostic that explains whether a page was restored successfully and highlights any blockers.

Imagine two stores:

  • Store A LCP: 1.8s, excellent Lighthouse score, Back navigation takes ~1 second every time.

  • Store B LCP: 2.0s, slightly lower Lighthouse score, Back navigation is instant.

Which one feels faster during a real shopping session? In many browsing-heavy journeys, Store B will feel faster despite having a slightly worse Lighthouse score.

Speed is not only about loading pages. It’s about maintaining the sense that the site is keeping up with the user.

The good news is that all major browsers support bfcache. The behavior differs slightly across browsers. Safari has historically been more aggressive about caching pages, while Chrome and Firefox enforce stricter eligibility requirements.

Handling restored state

The browser fires a pageshow event with event.persisted === true whenever a page is restored from bfcache.

In a Hyvä store, a clean pattern is to register a single Alpine component that owns all restore logic, then call it from init(). This keeps bfcache handling in one place rather than scattered across templates.

    
Alpine.data('bfCacheHandler', () => ({
    init() {
        window.addEventListener('pageshow', (event) => {
            if (event.persisted) {
                // Defer until after the browser finishes painting the restored page
                setTimeout(() => {
                    this.resetDesktopStates();
                    this.resetMobileStates();
                }, 0);
            }
        });
    },

    resetDesktopStates() {
        this.closeMinicart();
        this.closeSearch();
    },

    resetMobileStates() {
        this.closeMobileMenu();
        this.closeMinicart();
        // ... add other reset methods per your theme
    },

    closeMinicart() {
        const el = document.querySelector('.minicart-wrapper.active');
        if (el) el.click();
    },

    closeMobileMenu() {
        const toggler = document.querySelector('.navbar-toggler-icon.close-ico');
        if (toggler) toggler.click();
    },

    // ... add other reset methods per your theme
}));

The setTimeout(..., 0) defers the reset until after the browser has finished painting the restored page, avoiding a race between your cleanup and the bfcache restore itself.

Magento specific considerations

Magento 2 storefront pages are currently prevented from using bfcache by default due to two blockers in core: the Cache-Control: no-store header and multiple JS components that leave interactive elements in a broken state after restore.

The open Magento PR magento/magento2#40750 addresses both at the core level. It removes no-store from the PHP response and all Varnish VCL versions, and adds pageshow handlers to over a dozen components including:

  • minicart.js — closes the minicart dropdown and resets the loading counter
  • menu.js — collapses open submenus and clears mobile nav state
  • catalog-add-to-cart.js — resets button text and removes the disabled class
  • proceed-to-checkout.js — re-enables the checkout button
  • customer-data.js — re-runs the section staleness check so cart counts and customer names reflect changes made in other tabs
  • messages.js — clears cookie and customerData flash messages

For Hyvä themes stores, bfcache support has been available since version 1.4. Enable it under

Stores → Configuration → Hyvä Themes → System → Cache Options → Enable bfcache

then update your Varnish VCL to remove no-store. The Hyvä theme module already includes pageshow handlers for all native Hyvä components.

Bfcache is unusual among performance optimizations because it improves an interaction users perform constantly: going back. While teams often spend weeks chasing a few hundred milliseconds from first-page metrics, enabling bfcache can eliminate entire page loads from real user journeys. For many storefronts, it is one of the highest-ROI performance improvements available.

This article was updated on June 8, 2026