The back button costs nothing on 70 of our pages and a server render on the two that take money
Seventy of PubTrivia's 72 public pages are prerendered. The homepage and the pricing page are the two that render per request, because both read the visitor's country to choose a display currency, and both therefore answ
Seventy of PubTrivia's 72 public pages are prerendered. The homepage and the pricing page are the two that render per request, because both read the visitor's country to choose a display currency, and both therefore answer cache-control: private, no-cache, no-store.
I knew what that meant for the CDN. I had not thought about what it means for the back button, which is the single most used control in a browser. It turns out no-store is one of the things that keeps a page out of Chrome's back/forward cache, and Chrome will tell you so by name.
The measurement
Over CDP: enable the Page domain, load page A, load page B, then go back with Page.navigateToHistoryEntry and listen for Page.backForwardCacheNotUsed. If that event does not fire, the document was restored. I also set a variable on window before leaving, listened for pageshow, and counted the network requests the back navigation caused.
Going back to a prerendered page:
back to /faq: requests during back = 0, document responses = []
back to /guides/pub-quiz-format: requests during back = 0, document responses = []
Zero. No document request, no chunks, nothing. pageshow fired with persisted: true, window.__mark was still the string I had set before navigating away, performance.now() continued from where it left off, and the navigation timeline still reported navigate rather than back_forward, because it is the same navigation it always was. The document was frozen whole and thawed.
Going back to either of the two per-request pages:
back to /pricing: requests during back = 45, servedFromCache = 24, document = 200 MISS age:0
back to /: requests during back = 50, servedFromCache = 29, document = 200 MISS age:0
A fresh document, window.__mark gone, performance.now() restarted, navigation type back_forward, and a MISS on the document, which means a Vercel function ran and rendered the page again. Chrome's reasons, verbatim from the protocol:
Circumstantial: MainResourceHasCacheControlNoStore
Circumstantial: JsNetworkRequestReceivedCacheControlNoStoreResource
Circumstantial: BrowsingInstanceNotSwapped
The first one is the real one. The second is the same fact noticed again at the subresource level. Both of them trace back to one line in each of two files, export const dynamic = "force-dynamic", which exists so that a visitor in Sweden sees kronor.
What is actually lost, and what is not
My first draft of this post said the scroll position was lost. That was wrong, and the way it was wrong is worth recording.
Scroll restoration is a separate mechanism from the back/forward cache. Chrome stores the scroll offset in the history entry, so even on a document it has to rebuild from scratch it puts you back where you were. I measured 900 pixels before leaving /pricing and 900 pixels a second after coming back, on a document that was demonstrably rebuilt.
The first measurement that said otherwise had two faults, both mine. I read scrollY too soon after scrollTo(), and this site sets html { scroll-behavior: smooth }, so an immediate read reports a position the page is still travelling towards rather than the one it will land on. Then the history entry recorded the stale offset. If you measure scrolling in a browser, wait for it, and read the value twice.
So the user-visible damage from missing the back/forward cache is not a jump to the top of the page. It is this:
- 45 to 50 requests, roughly half of which do hit the HTTP cache, but the document never can, because
no-store - a serverless function invocation on every back press, on the two pages a visitor bounces back to most while comparing plans
- every bit of client state on the page thrown away, which on the pricing page includes which plan card the visitor had focused
- the paint is a fresh render rather than a restore, which is the difference between a frame and a round trip to Washington, where our functions run
Is it fixable
Chrome has been working on allowing no-store pages into the back/forward cache, with eviction if the cookies change while the page is frozen. That would help us for free one day, and it is not something to plan around.
The real fix is not to be no-store. The currency decision could move to the client, or the pricing page could be prerendered per currency, both of which have their own costs that an earlier post went through and neither of which is free. What has changed today is that the trade now has a number attached: two pages out of 72 cannot be restored, and each unrestored back press is one server render.
Everything else on the site, including every guide, every comparison and every free tool, comes back for nothing.
Check it yourself, no CDP required
- Open pub-trivia.app/faq, open the console, and type
window.x = 42. - Click any link, then press the browser's back button.
- Type
window.x. It is still42: same document, restored from the back/forward cache.
Now do the same thing starting from pub-trivia.app/pricing. After the back button, window.x is undefined, and the Network panel shows the whole page coming down again with the document marked x-vercel-cache: MISS. Two pages on the site behave one way and seventy behave the other, and the only difference between them is one exported constant.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.