A couple of months ago I worked on an issue stating that users using iOS where getting an unmodified result despite modifying input payload. Even with proper Cache-Control-headers.

This turned out to be a result of the Back-Forward Cache, or bfcache, in the browser which is serving a cached state and omits an extra roundtrip to the server. This mechanism is used to increase perceived speed and are in effect when the user is using the back and forward buttons to navigate.

While being a valid browser optimization, it can be the source of some weird behaviour in our Javascript applications, especially when utilizing Javascript for state management and routing. Some of the most popular browsers have internal cache-busting mechanisms when detecting alterations of the state, but Mobile Safari is a tougher player when dealing with cache invalidation.

To solve this we needed to force the browser to update its state. Given the state served via cache, not all events were fired as expected. There are a couple of ways to deal with this and the most popular one being triggering a function on the unload event, but we found that using the pageshow event was a much cleaner solution.

The event object received at pageshow contains a property indicating whether the current user state is persisted or not1 which is perfect for this use case.

  window.addEventListener('pageshow', e => {
    if (e.persisted) {
        // The user state is persisted and served from bfcache.

Our solution was to do a forced reload of the document if the state was persisted. Our final code looked something like this:

  window.addEventListener('pageshow', e => {
    if (e.persisted) document.location.reload()