97

Got an issue with safari loading old youtube videos when back button is clicked. I have tried adding onunload="" (mentioned here Preventing cache on back-button in Safari 5) to the body tag but it doesn't work in this case.

Is there any way to prevent safari loading from cache on a certain page?

Community
  • 1
  • 1
Mark Steggles
  • 5,369
  • 8
  • 40
  • 50
  • possible duplicate of [Mobile Safari back button](http://stackoverflow.com/questions/11979156/mobile-safari-back-button) – Mika Tuupola Feb 27 '14 at 21:52
  • @MikaTuupola answer should be marked as the correct answer. – Julien Bérubé Apr 15 '14 at 17:40
  • This is the humble problem I also experienced because of the same BFCache behavior on Safari: http://stackoverflow.com/questions/40727989/is-onbeforeunload-cached-on-safari-macos/40896361?noredirect=1#comment69043078_40896361 – juanmirocks Dec 01 '16 at 18:10
  • In my case, when I just had to manipulate some classes, the code in [this article](https://guwii.com/cache-issues-with-forwards-and-back-history-in-safari/) worked. – Barnee Aug 06 '20 at 11:28

8 Answers8

235

Your problem is caused by back-forward cache. It is supposed to save complete state of page when user navigates away. When user navigates back with back button page can be loaded from cache very quickly. This is different from normal cache which only caches HTML code.

When page is loaded for bfcache onload event wont be triggered. Instead you can check the persisted property of the onpageshow event. It is set to false on initial page load. When page is loaded from bfcache it is set to true.

Kludgish solution is to force a reload when page is loaded from bfcache.

window.onpageshow = function(event) {
    if (event.persisted) {
        window.location.reload() 
    }
};

If you are using jQuery then do:

$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        window.location.reload() 
    }
});
Mika Tuupola
  • 19,877
  • 5
  • 42
  • 49
  • 9
    This is the only working solution I have found so far, but it's rather imperfect as the page is still shown briefly before reloading, and the solution is easily thwarted by disabling javascript. Has anyone managed to find a more complete solution? – lukens Mar 17 '16 at 12:01
  • This does not seem to work on Chrome for Android (not testing any other mobile browser so limiting my statement) – Dominique Feb 08 '17 at 17:57
  • For IE 11, I had to use `window.location = window.location` instead of `window.location.reload()`, for reasons that I assume are lost to the mists of time. – Paul D. Waite Jun 06 '18 at 17:20
  • To solve the issue with page visibility just before the onpageshow istrigger you can use onbeforeunload event. During this event you can change body's opacity to 0. So yes, but page will be briefly visible, but with 0 opacity. (window.onbeforeunload = () => { document.body.opacity = 0; } ). This works with Safari 11. – Kamen Stoykov Sep 16 '18 at 14:50
  • Does not work in Chrome (desktop too) because of a bug https://bugs.chromium.org/p/chromium/issues/detail?id=344507&can=5&colspec=ID%20Pri%20M%20Iteration%20ReleaseBlock%20Cr%20Status%20Owner%20Summary%20OS%20Modified – lofihelsinki Mar 07 '19 at 13:10
  • There is a bug in Safari as documented here: https://bugs.webkit.org/show_bug.cgi?id=156356 The Safari "pageshow" / "pagehide" events don't get fired more than once IF an iFrame is present on the page. In my case it's the Facebook "Like button" iFrame. Pretty annoying! – Bob Nov 19 '19 at 09:52
  • No longer works for the latest Safari in iOS 14.2 – RainCast Dec 10 '20 at 10:45
  • @RainCast did you mean the fix(es) no longer work(s) for the latest Safari, or the original issue is not reproducible anymore? – natonomo Jan 11 '21 at 23:17
15

All of those answer are a bit of the hack. In modern browsers (safari) only on onpageshow solution work,

window.onpageshow = function (event) {
    if (event.persisted) {
        window.location.reload();
    }
};

but on slow devices sometimes you will see for a split second previous cached view before it will be reloaded. Proper way to deal with this problem is to set properly Cache-Control on the server response to one bellow

'Cache-Control', 'no-cache, max-age=0, must-revalidate, no-store'

waj-er-rr
  • 159
  • 1
  • 2
  • Thanks the header seems to work and be much nicer than the JS solutions. – Andy Dec 19 '19 at 09:57
  • I placed this code in the HEADER and now Safari on iPhone does execute JS and JQuery code when going back to this page via the Back Button, Thanks! – Al-Noor Ladhani Feb 11 '20 at 23:08
  • Setting the Cache-Control is the only thing that seems to work on Chrome. There is no need for all the directives, as this is equivalent to just `Cache-Control: no-store`. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#preventing_storing – Nathan Jul 27 '23 at 17:25
7

Yes the Safari browser does not handle back/foreward button cache the same like Firefox and Chrome does. Specially iframes like vimeo or youtube videos are cached hardly although there is a new iframe.src.

I found three ways to handle this. Choose the best for your case. Solutions tested on Firefox 53 and Safari 10.1

1. Detect if user is using the back/foreward button, then reload whole page or reload only the cached iframes by replacing the src

if (!!window.performance && window.performance.navigation.type === 2) {
            // value 2 means "The page was accessed by navigating into the history"
            console.log('Reloading');
            //window.location.reload(); // reload whole page
            $('iframe').attr('src', function (i, val) { return val; }); // reload only iframes
        }

2. reload whole page if page is cached

window.onpageshow = function (event) {
        if (event.persisted) {
            window.location.reload();
        }
    };

3. remove the page from history so users can't visit the page again by back/forward buttons

$(function () {
            //replace() does not keep the originating page in the session history,
            document.location.replace("/Exercises#nocache"); // clear the last entry in the history and redirect to new url
        });
Javan R.
  • 2,011
  • 1
  • 19
  • 16
3

I had the same issue with using 3 different anchor links to the next page. When coming back from the next page and choosing a different anchor the link did not change.

so I had

<a href="https://www.example.com/page-name/#house=house1">House 1</a>
<a href="https://www.example.com/page-name/#house=house2">View House 2</a>
<a href="https://www.example.com/page-name/#house=house3">View House 3</a>

Changed to

<a href="/page-name#house=house1">House 1</a>
<a href="/page-name#house=house2">View House 2</a>
<a href="/page-name#house=house3">View House 3</a>

Also used for safety:

// Javascript
window.onpageshow = function(event) {
    if (event.persisted) {
        window.location.reload() 
    }
};

// JQuery
$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        window.location.reload() 
    }
});

None of the solutions found online to unload, reload and reload(true) singularily didn't work. Hope this helps someone with the same situation.

user3615851
  • 999
  • 1
  • 15
  • 40
2

You can use an anchor, and watch the value of the document's location href;

Start off with http://acme.co/, append something to the location, like '#b';

So, now your URL is http://acme.co/#b, when a person hits the back button, it goes back to http://acme.co, and the interval check function sees the lack of the hash tag we set, clears the interval, and loads the referring URL with a time-stamp appended to it.

There are some side-effects, but I'll leave you to figure those out ;)

<script>
document.location.hash = "#b";
var referrer = document.referrer;

// setup an interval to watch for the removal of the hash tag
var hashcheck = setInterval(function(){
    if(document.location.hash!="#b") {

    // clear the interval
    clearInterval(hashCheck);

    var ticks = new Date().getTime();
    // load the referring page with a timestamp at the end to avoid caching
    document.location.href.replace(referrer+'?'+ticks);
    }
},100);
</script>

This is untested but it should work with minimal tweaking.

Larry Williamson
  • 1,149
  • 5
  • 18
1

The behavior is related to Safari's Back/Forward cache. You can learn about it on the relevant Apple documentation: http://web.archive.org/web/20070612072521/http://developer.apple.com/internet/safari/faq.html#anchor5

Apple's own fix suggestion is to add an empty iframe on your page:

<iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
    this frame prevents back forward cache
</iframe>

(The previous accepted answer seems valid too, just wanted to chip in documentation and another potential fix)

Simon Boudrias
  • 42,953
  • 16
  • 99
  • 134
  • Best solution I found was here: http://stackoverflow.com/questions/20899274/how-to-refresh-page-on-back-button-click – i_a Oct 31 '15 at 09:34
0

First of all insert field in your code:

<input id="reloadValue" type="hidden" name="reloadValue" value="" />

then run jQuery:

jQuery(document).ready(function()
{
        var d = new Date();
        d = d.getTime();
        if (jQuery('#reloadValue').val().length == 0)
        {
                jQuery('#reloadValue').val(d);
                jQuery('body').show();
        }
        else
        {
                jQuery('#reloadValue').val('');
                location.reload();
        }
});
0

There are many ways to disable the bfcache. The easiest one is to set an 'unload' handler. I think it was a huge mistake to make 'unload' and 'beforeunload' handlers disable the bfcache, but that's what they did (if you want to have one of those handlers and still make the bfcache work, you can remove the beforeunload handler inside the beforeunload handler).

window.addEventListener('unload', function() {})

Read more here:

https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/1.5/Using_Firefox_1.5_caching

B T
  • 57,525
  • 34
  • 189
  • 207