The Back Button Does Not Always Reload: Auditing bfcache After `no-store`
Chrome can restore some `Cache-Control: no-store` pages from bfcache. Learn how to test logout, stale data, forms, analytics, and page lifecycle.
The browser Back button looks simple: leave a page, go back, load the page again. For modern browsers, that mental model is often wrong.
Browsers can keep a page in the back/forward cache (bfcache) as a complete in-memory snapshot. When the user navigates backward or forward, the browser may not perform a normal reload at all. The DOM, JavaScript heap, and page state can reappear while JavaScript execution, paused when the user left, resumes.
That is excellent for performance. It can also expose fragile assumptions when an application expects every return visit to trigger a fresh request, a new authorization check, another analytics event, or a complete data fetch.
And since 2025, one common assumption is even less reliable: in Chrome, Cache-Control: no-store no longer automatically means “this page can never return from bfcache.”
What Chrome changed
Chrome expanded bfcache eligibility for certain pages that use Cache-Control: no-store. Chrome for Developers says the final rollout reached 100% of users during March and April 2025.
This is not Chrome ignoring sensitive-data concerns. It applies additional safeguards. Among other things, Chrome evicts affected no-store pages from bfcache when cookies or other authorization mechanisms change. APIs such as WebSocket, WebTransport, and WebRTC continue to make these no-store pages ineligible, and the bfcache timeout for them is shorter.
The architectural point remains important:
The HTTP cache and bfcache are different mechanisms.
Cache-Control: no-store primarily controls storage of HTTP responses. bfcache preserves the state of an already loaded page in memory. If your application used no-store as a universal “Back button protection,” that assumption now deserves an explicit test.
Why ordinary websites should care
This sounds like browser-engine trivia until it meets a real user journey.
1. Logout and authenticated pages
A user opens their account, navigates elsewhere, logs out in another tab, then returns with the Back button.
The correct outcome is obvious: protected information must not be treated as an active authorized session.
Chrome has safeguards around cookie and authorization changes for no-store pages, but that is not a substitute for application testing. Authentication may involve cookies, tokens, server-side sessions, client-side state, and third-party SDKs. The meaningful question is not whether Chrome has a heuristic. It is whether your real logout flow reliably prevents a restored interface from behaving as if the user were still authorized.
2. Stale data after Back
A pricing page, basket, booking list, or admin dashboard can return exactly as the user left it a minute ago.
Sometimes that is ideal. A visitor returning to a long article list probably wants the same scroll position and filters.
For dynamic data, the same behavior can be wrong:
- An order was cancelled elsewhere.
- A permission was revoked.
- The basket changed in another tab.
- Availability is no longer current.
- A form suggests an action is still possible.
- A dashboard shows values that became stale after a mutation.
The answer is not to disable bfcache everywhere. The answer is to distinguish between state that should be preserved and state that must be revalidated when the page becomes active again.
3. Analytics may miss or misclassify return navigation
A bfcache restore is not a normal full page load. If pageviews are emitted only from load, a framework mount, or initial startup code, back/forward navigation may be measured differently from what the analytics model expects.
web.dev recommends accounting for the pageshow event. It fires when a page is restored from bfcache, and event.persisted can indicate that the page came from a preserved state.
That does not mean every pageshow should automatically fire another pageview. The correct behavior depends on the measurement model. The important part is making a deliberate decision about whether history navigation counts as a view, a navigation event, or neither.
4. Forms can look “half submitted”
A user completes a form, submits it, reaches a confirmation page, and presses Back.
Depending on the implementation, the previous page may return with old input values, disabled buttons, local validation state, or an outdated success/error message. The dangerous version is an interface that appears to offer an action again even though the server already processed it.
Business-critical actions therefore need server-side idempotency or an equivalent transaction model where duplicate execution would cause harm. Browser navigation should never be the only protection against repeated actions.
5. Single-page applications overestimate their lifecycle
React, Vue, Next.js, and similar stacks can encourage the assumption that components will remount cleanly whenever the user “comes back.” A bfcache restore can instead resume the existing document state.
Code that runs only during initialization is therefore not automatically the right place for every freshness check. Critical state should be tied to the actual page lifecycle.
The useful audit question: what really happens when the user goes Back?
A bfcache audit should not stop at asking whether a page is cacheable. It should answer two questions:
- Can the page benefit from bfcache?
- Do security, data freshness, measurement, and business logic remain correct when it is restored?
Both goals matter. Forcing a page out of bfcache because the application does not understand its own lifecycle throws away performance. Optimizing blindly for bfcache without testing sensitive state creates functional risk.
Eight practical test cases
1. Sign in → protected page → sign out → Back
Run the flow in one tab, then repeat it with the logout occurring in another tab. Do not only inspect the UI. Attempt a real protected action.
Expected result: no stale interface should imply valid authorization, and protected requests must still be rejected server-side.
2. Change data → navigate away → Back
Modify a record from another tab or session, then return through browser history.
Ask: must the visible state update immediately, or is the preserved state acceptable for this workflow?
3. Submit a form → Back
Inspect fields, validation messages, loading state, and the submit action. Pay special attention to operations that must not execute twice.
4. Change consent state
Modify cookie or tracking preferences, navigate away, and return. Verify that embeds, tag management, and the visible consent UI represent the current state.
5. Switch language, theme, or account
Global settings are often stored client-side. A restored snapshot should not indefinitely show the previous identity, language, or theme when the underlying state changed.
6. Analytics and performance measurement
Compare a normal navigation with a back/forward restore. Are pageviews counted twice, not at all, or intentionally separated? Are performance metrics being interpreted as if every navigation were a cold page load?
7. Real-time connections
WebSocket, WebRTC, and related connections have their own lifecycle requirements. Verify whether they reconnect correctly, pause safely, or intentionally make the page ineligible for bfcache.
8. Multiple browsers
Chrome, Safari, and Firefox all implement bfcache, but their eligibility rules are not identical. A Chrome-specific safeguard is not a cross-browser contract.
Use pageshow for targeted revalidation
For pages that need to verify state after a restore, pageshow is a useful lifecycle point.
When event.persisted is true, the page has returned from a preserved state. That is an opportunity to revalidate only the data that genuinely needs to be current:
- session or account state
- critical permissions
- basket or booking state
- time-sensitive pricing or availability
- consent configuration
- server-side data changed elsewhere
The key word is targeted. Hard-reloading the whole application on every Back click destroys much of the performance benefit bfcache is designed to provide.
A better pattern is to let the browser restore the visual state instantly, then revalidate sensitive data in the background. If something changed, update the interface deliberately.
Why unload is not a good escape hatch
Legacy code often attaches cleanup logic to unload. That is a poor foundation today.
web.dev warns that unload is unreliable and can prevent bfcache usage in some browsers. Depending on the task, pagehide, visibilitychange, and pageshow are better lifecycle primitives.
So the goal should not be: “How can we break bfcache using an old event?”
It should be: “How do we make our page state correct when a page is paused and later resumed?”
Testing bfcache in Chrome DevTools
Chrome DevTools provides a dedicated back/forward cache test in the Application panel. It can attempt a bfcache restore and report reasons why a page could not be restored.
That is useful for two opposite classes of defect:
- A page should benefit from instant history navigation but unnecessary code prevents it.
- An application appears correct but has never been tested under a real preserved-state restore.
For production telemetry, the notRestoredReasons API can also provide information about why a history navigation was not served from bfcache. This becomes valuable when real users have browser, iframe, or third-party combinations that local tests do not reproduce.
Audit red flags
Cache-Control: no-storeis treated as the only protection against back navigation.- Logout is tested only by clicking the button, never by navigating Back afterward.
- Authorization is represented as frontend visibility rather than enforced server-side permission.
- Critical data is fetched only on initial load.
- Analytics has no model for bfcache restores.
- Forms can accidentally repeat an operation after history navigation.
- Third-party code registers
unloadhandlers and silently blocks bfcache. - Nobody tests Safari or Firefox because the Chrome test passed.
- Teams disable bfcache globally instead of fixing the underlying state model.
What Website-Pflichtencheck would inspect
A bfcache and history-navigation review can examine:
- actual bfcache behavior across representative page types
Cache-Controlheaders and their intended purpose- login, logout, and role changes
- behavior after session or consent changes
- forms and non-repeatable actions
- data freshness after back/forward navigation
pageshow,pagehide,visibilitychange, and problematicunloadhandlers- analytics and performance measurement
- WebSocket and real-time components
- differences between Chrome, Safari, and Firefox
- DevTools and Lighthouse findings
- regression tests for critical user journeys
bfcache is not a browser defect to switch off. It is a real page-lifecycle behavior that modern websites need to handle.
The Back button no longer guarantees a new page load. If your security, data, or measurement logic depends on that assumption, the fragile part is not the browser — it is the assumption.