A user presses the browser's Back button and the previous page appears instantly — scroll position and typed form values intact, and the Network panel shows no request for the document. What is the browser's back/forward cache doing here, and does the page's JavaScript start over?
answer
- Back is undo, not a new load
- whole document frozen, not cached bytes
- heap, DOM, scroll, form values survive
- load fires once per document lifetime
- pageshow is the restore signal
basics
~20 sThe back/forward cache keeps the whole page — DOM plus the JavaScript heap — frozen in memory when you navigate away, then restores that snapshot on Back. Scripts do not re-run, and DOMContentLoaded and load do not fire again.
solid answer
~50 sThe back/forward cache (bfcache) is an in-memory snapshot of a **complete, live document**: the DOM, the CSSOM, the JavaScript heap with all its variables and closures, scroll position, form control values and pending timers. When the user navigates away, the browser freezes the page instead of destroying it — the event loop for that document simply stops running tasks. On a back or forward traversal, the browser puts that frozen document straight back on screen: no document request, no re-parse, no script re-execution. `DOMContentLoaded` and `load` fire exactly once in the page's life and do **not** fire again on restore; the signal for a restore is the `pageshow` event with `event.persisted === true`. So any setup you wrote inside a `load` handler runs once and never again, which is why clocks, polling and "fetch fresh data on start" logic silently go stale after a Back.
go deeper
Be able to say plainly that a Back navigation can restore a frozen copy of the page instead of loading it again, so scripts do not re-run and the page comes back exactly as the user left it.
Explain the mechanics: the document's event loop is paused, the JavaScript heap and DOM survive intact, and pageshow with event.persisted is the only signal that a restore happened rather than a load.
Show that you design for both paths — code must be correct on a cold load and on a restore of a page frozen minutes ago — and that you know a cached entry can be evicted for memory or age at any time.
Own the tradeoff of keeping pages eligible: it is real, measurable speed, but it means long-lived in-memory state can reappear on screen, so it changes what your team may safely keep in a page and what must be re-validated on restore.
## What problem bfcache solves Before the back/forward cache existed, pressing Back was an ordinary navigation: the browser fetched or revalidated the document, re-parsed the HTML, re-executed every script, re-hydrated the framework and re-issued all the API calls. Users do not think of Back as a navigation — they think of it as undo — so that round trip felt broken. The back/forward cache makes a history traversal look like what users expect: the previous page, instantly, exactly as they left it. ## What is actually stored bfcache does not store bytes of a response. It stores a **live document, paused**. That includes: - the DOM tree and the CSSOM, with any DOM mutations your script had already made, - the entire JavaScript heap for that document: module-level variables, closures, class instances, in-memory caches, framework state, - scroll position of the document and of scrollable elements, - the current values of form controls, including ones the user typed but never submitted, - pending timers and animation state, suspended rather than cleared. This is why it is often described as "freezing" the page. It is not the HTTP cache. The HTTP cache stores response bytes so the network can be skipped; bfcache stores a running document so *everything* — parsing, execution, rendering, data fetching — can be skipped. ## Freeze and restore, step by step When the user navigates away and the browser decides the page is eligible, the document's event loop stops: no tasks, no timers, no `requestAnimationFrame` callbacks, no observer callbacks. Nothing in the page runs while it sits in the cache. The last thing it sees on the way out is a `pagehide` event. On a back or forward traversal to that entry, the browser reinstates the document and fires `pageshow`. The `PageTransitionEvent` passed to that handler carries a `persisted` property, and `persisted === true` means "this is a bfcache restore, not a fresh load". ```js window.addEventListener('load', () => { startClock(); // runs once, never again after a bfcache restore }); window.addEventListener('pageshow', (event) => { if (event.persisted) { startClock(); // runs on every restore too } }); ``` The practical rule: initialisation that must happen *every time the page becomes visible again* cannot live only in `load` or in top-level module code, because neither runs a second time. ## What does not happen On a restore there is **no** request for the document, **no** HTML parse, **no** script evaluation, and **no** `DOMContentLoaded` or `load`. Framework mount hooks do not run again either, because the components were never unmounted — they were frozen mid-life. Nothing was reset, so nothing needs to be rebuilt. ## It is an optimisation, not a guarantee A page can be evicted from the cache at any moment: memory pressure, too many entries, or simply age — Chrome discards bfcache entries after roughly ten minutes. A page may also be ineligible in the first place because of what it does (an `unload` listener, a `Cache-Control: no-store` document response, certain open connections). So correct code must work on **both** paths: a restore, and an ordinary cold load of the same URL. Write the page so a full reload is always acceptable, and treat bfcache as speed on top of that. ## Which navigations qualify Only history traversals in the same tab — the Back and Forward buttons, `history.back()`, `history.forward()`, a swipe gesture, or a keyboard shortcut for those. Clicking a link to a URL you visited earlier is a *new* navigation and gets a fresh document; so is a reload, a bookmark, and a typed address. That distinction matters when you are trying to reproduce the behaviour: you must actually traverse history, not re-request the URL. ## Why interviewers ask bfcache eligibility has become a metric teams are asked to defend, because a restore is the cheapest possible "page load" and directly improves how the site feels. Candidates who only know "Back is fast now" miss the consequence that matters in code review: your page keeps running with state from minutes ago, and any assumption that a visible page was recently initialised is wrong.
- If nothing re-runs on a restore, how does a page find out it was restored at all?Through the `pageshow` event. It fires both after an ordinary load and after a bfcache restore, and `event.persisted` distinguishes them — `true` means the document came out of the back/forward cache. Setup that must repeat on every appearance belongs in that handler, not only in a `load` listener or top-level module code.
- Is bfcache the same thing as the HTTP cache serving the document from disk?No. The HTTP cache stores response bytes; the browser still parses the HTML and re-executes all scripts, so the page starts from zero state. bfcache stores a paused, fully-constructed document — heap, DOM and scroll included — so there is nothing to parse or execute. A page can be HTTP-cacheable and still bfcache-ineligible.
- Does a bfcache restore ever happen for a link click to a page the user visited earlier?No. Only history traversals qualify — Back, Forward, `history.back()`, `history.go()`, or the equivalent gesture. A link click, a reload, a bookmark or a typed URL creates a new navigation entry and gets a freshly loaded document, even if the same URL is already sitting in the cache.
saying these in an interview costs you the question
- Says the scripts re-run from the top on a Back restore
- Confuses bfcache with the HTTP disk cache for the document
- Assumes DOMContentLoaded or load fires again on restore
- Thinks any repeat visit to a URL uses bfcache
- Treats bfcache as guaranteed rather than best-effort