Recognise a restored page

A shopper may leave checkout to inspect a product, alter the cart in another tab, or follow a payment provider and then press Back. The browser may restore the previous document from its back/forward cache, often called bfcache. This is a paused page in memory, including its old interface state, rather than a fresh response from the shop. An apparently instant return is useful, but the figures and buttons on screen may describe an earlier cart or earlier session. Treat the visible page as a candidate view until the shop has reconciled it with current server state.

Google's web platform guidance says a pageshow event occurs on initial load and on bfcache restoration. Its persisted flag distinguishes a restored page when true. A pagehide event can tell a page that it may enter the cache, but a true persisted value there is not a promise that restoration will happen. Build a return check around pageshow and the ordinary fresh-load path; do not make correctness depend on a particular browser saving the page. The same customer journey must still work when the browser reloads instead of restoring it.

Choose which state is authoritative

The order service should decide the current cart lines, quantities, applicable price, delivery method, availability and payment status. A restored DOM, local JavaScript variable or old button label cannot authorise a transaction. At return, fetch the current checkout summary with the customer's active session and an order or cart identifier that the server validates. Compare it with the rendered summary. If the customer changed quantities elsewhere, show the new quantities and explain why the total changed. If the session ended, show a sign-in route and preserve only the safe route back, subject to the shop's account policy.

A payment return deserves a separate check. The provider redirect, browser history and a customer's claim are observations, not the shop's final payment record. Ask the server for the order's current state and present pending, paid or failed as distinct outcomes. If the payment is still being confirmed, suppress another charge attempt until the service can offer a safe recovery choice. If an order was already placed, a back navigation must not turn the old checkout button into a second order submission. Repeated requests should resolve against the existing order identity and the shop's established duplicate-prevention controls.

Make the correction visible

A silent refresh can also mislead. Suppose a customer returns to a checkout showing two items for an illustrative HK$600, while a separate tab has removed one item. The server now reports one item for an illustrative HK$350. These amounts are only an example, not a measured price or offer. Replace the old summary, identify the removed item and require the customer to review the new payable total before continuing. If the total or delivery terms change, retain the earlier choice only when it remains valid. The important test is whether the customer can say what changed and which total they are agreeing to.

Keep the page usable while the check runs. Mark the order summary as updating and temporarily prevent the final pay or place-order action from using stale data. If the check fails because the connection is unavailable, state that the current total cannot be confirmed and offer a retry or safe cart return. Do not present an old amount as final just because it was already painted on screen. Preserve harmless form entries where possible, but distinguish them from server-confirmed choices. A visible status message and a clear focus path matter especially when an update changes content after the page reappears.

Separate privacy from speed

The bfcache is distinct from the HTTP cache. A Cache-Control instruction that makes an HTTP response revalidate does not itself refresh a document restored from memory. Google's guidance specifically calls out old checkout data and private information after sign-out as risks on restoration. Do not weaken a response policy merely to improve history navigation. Pages containing genuinely sensitive data may warrant no-store or a different design, while less sensitive pages can remain eligible for restoration if a robust return reconciliation is in place. Browser treatment of no-store also changes over time, so verify the actual browsers serving the shop.

After sign-out, a restored account or checkout page must not expose usable personal details or active controls to the next person using that device. The application should validate the session before revealing protected state, clear sensitive client state when the session is invalid, and route the person to sign-in or a neutral page. Test the visible interval before any asynchronous refresh finishes, not only the eventual screen. If the product has a policy requiring no sensitive data to remain in browser history at all, make that requirement explicit and design the response and navigation around it rather than assuming pageshow will erase information before it is seen.

Implement one return path

Give the checkout a small return coordinator: when the page becomes visible, determine whether it was restored, block consequential actions while status is uncertain, request the current summary, reconcile it, then announce the resulting state. A fresh load should use the same server data and final-action rules. Avoid adding an unload handler just to force a reload; that can interfere with bfcache in some browsers and still does not define a correct recovery flow. Close or release long-lived connections on pagehide where appropriate, then reconnect on pageshow without opening duplicates. Keep the logic idempotent because visibility or lifecycle events can repeat.

Keep your analytics interpretation separate from user correctness. A bfcache restoration is a history navigation, not necessarily a new document load. If the shop counts checkout visits or abandonment, define whether a restored visit counts as another view and implement that definition consistently. Do not infer a sales lift from a faster screen without measured evidence. The operational objective here is narrower: no stale final total, no second order from history, no private data after sign-out, and a route back to a valid choice. Those are observable acceptance results the team can test before discussing any performance benefit.

Test the journeys that change state

Use a test cart and record the server state before each navigation. First, move from checkout to a product page and Back with no change; the original choices should remain valid. Next, change a quantity in another tab and Back; the new total should appear before the final action is enabled. Repeat with stock becoming unavailable and with delivery eligibility changing. Then complete a test order, navigate Back, and confirm the old form cannot submit another order. Finally, sign out in another tab and use Back: protected details should not remain visible or actionable.

Run those journeys in the browsers and devices customers actually use. Inspect pageshow.persisted in a development session, but include a case where restoration does not occur; the outcome should agree. Record what the customer saw during the refresh, whether the total and payment state matched the server, and whether a screen-reader user heard a useful status. If the browser's developer tools explain why a page was not restored, that can guide an optimisation later. It does not replace the core test: a customer must reach the correct order state regardless of the cache path chosen by the browser. Include a slow connection during the return check: the old total must never become actionable while the request waits. Then simulate the status endpoint failing after the page appears. The customer should still have an honest route back to the cart, and support should be able to identify the existing order without initiating another charge. This tests the uncertain interval that a quick happy-path demonstration often misses.

Give support a state trail

A support agent should be able to answer a customer who says, “I went back and the price changed,” without treating the old screenshot as proof of a charge. Record a safe order or cart reference, the server summary version returned on reconciliation, and the outcome shown to the customer. Keep payment references separate from card details and never ask the customer to send a secret. If there was a legitimate price change, explain its source in the actual order record; if the display was stale, acknowledge the display problem and identify the confirmed amount before offering another action.

Keep the reconciliation record focused. It need not duplicate a whole cart containing personal details in analytics events. A timestamp, route, state transition and nonsecret reference can let a team reproduce the problem while respecting the shop’s data minimisation choices. Check that retries of the return query do not generate fresh orders and that support cannot override payment status from a visual report alone. This turns a browser lifecycle edge case into a manageable service journey: the customer can understand the corrected state, and staff can trace how that state was reached.

Before you proceed

  • Can the final action wait for a current server summary?
  • Does Back after payment resolve to the existing order?
  • Does sign-out prevent old private data being seen or used?

Sources & further reading