4

My site is constructed entirely of dynamic data, some of which changes based on user authentication. When a user "logs in" to the site, I show their new status with their user name in the page navigation bar. However when they visit a page they visited recently before authenticating (back button, etc), that page's version of the navigation bar will not be refreshed. What are some methods to force a refreshed page to the browser? The inverse is also true: if a user logs out, recent pages will still show authenticated from the cached version.

What technique can I use to always force a browser-side refresh/clear cache on any page on the website?

Thanks.

Nb: server side I am using eXist-db 4.7's login:set-user() to authenticate a user (i.e. "log them in") through the controller.

jbrehr
  • 775
  • 6
  • 19

3 Answers3

2

I suggest you to check out this thread, for some more specific information, but to sum it up:

  • You need to check if the loginState changed
  • You need to refresh the page if it did

Solution 1)

Step 1

You need to initialize a global variable like this:

let loginStateChanged = false;

And when the user logs in or out, you do:

loginStateChanged = true;

Step 2

You need to listen for "browser back-button events" and refresh the window if the login state has changed.

You can use the pageshow event like so:

window.addEventListener('pageshow', (event) => {
    var isBackNavigation = event.persisted || 
        (typeof window.performance != 'undefined' && window.performance.navigation.type === 2);

    // If the navigation type is a back navigation, and the login
    // State has changed, refresh the window
    if (isBackNavigation && loginStateChanged) {
        window.location.reload(); // reload the page
    }
});

Or with jQuery;

$(window).on('popstate', () => {
    // If the login State has changed, refresh the window
    if (loginStateChanged) {
        window.location.reload(); // reload the page
    }
});

Becuase the window refreshed, the loginStateChanged will be reset to its default value false.

Solution 2)

Or if you need to implement the solution for multiple tabs:

Step 1

Use localStorage and a global variable instead:

let isLoggedIn = JSON.parse(localStorage.getItem('isLoggedIn')); // Get the initial value
isLoggedIn = isLoggedIn != 'undefined' ? isLoggedIn : false; // If initial value is undefined, set it to false

And when the user logs in or out, you do:

isLoggedIn = /* login: true, logout: false */; // update global variable
localStorage.setItem('isLoggedIn', JSON.stringify(isLoggedIn)); // update localStorage variable

Step 2

window.addEventListener('pageshow', (event) => {
    var isBackNavigation = event.persisted || 
        (typeof window.performance != 'undefined' && window.performance.navigation.type === 2);

    // If the navigation type is a back navigation, and the login 
    // State has changed, refresh the window
    // Here you check, if the localStorage variable is different, than the global variable
    if (isBackNavigation && JSON.parse(localStorage.getItem('isLoggedIn')) != isLoggedIn) {
        window.location.reload(); // reload the page
    }
});

Solution 3)

If you want to refresh on any back or forward click, just use this code:

window.addEventListener('pageshow', (event) => {
    var isNavigation = event.persisted || 
        (typeof window.performance != 'undefined' && window.performance.navigation.type === 2);
    if (isNavigation) {
        window.location.reload();
    }
});

Solution 3 Test-files:

test1.html and test2.html (they are exactly the same):

<html>
    <head>
        <title>test</title>
        <script>
            window.addEventListener("pageshow", function ( event ) {
                var isNavigation = event.persisted || 
                    (typeof window.performance != 'undefined' && window.performance.navigation.type === 2);

                console.log("Is navigated through button? ", isBackNavigation);
                if (isNavigation) {
                    alert("reload!");
                    window.location.reload();
                }
            });
        </script>
    </head>
    <a href="test1.html">Test1</a>
    <a href="test2.html">Test2</a>
</html>

It will refresh the whole page on every navigation action. And on the opening of a new tab it will reload it anyways.

MauriceNino
  • 6,214
  • 1
  • 23
  • 60
  • This would force the page to refresh on every back press as opposed to when the login state changes. I'd suggest you could have an initial 'loginState' on each page that has whether or not the user was logged in during the page load. When the user logs in or logs out this should change a local storage variable. Then your answer could be amended to include a check to see if the initial login state still matches the local-storage value to decide whether to refresh or not. – Royal Wares Aug 29 '19 at 10:46
  • @AlexanderDeSousa You are right, I misread that. Made some changes, that should result in the same outcome, than what you described, but without localStorage. – MauriceNino Aug 29 '19 at 10:56
  • Without localStorage it means you need to have some sort of AJAX polling to change that state, this would be inefficient if the user has multiple tabs/windows open. If you set the localStorage state at the login/logout event then the browser becomes aware of the change everywhere at the same time. – Royal Wares Aug 29 '19 at 12:49
  • @AlexanderDeSousa For multiple tabs you are right. But it has nothing to do with AJAX. – MauriceNino Aug 29 '19 at 13:17
  • Thanks for these solutions - I'll have to test them out before accepting for bounty... – jbrehr Aug 29 '19 at 14:19
  • @jbrehr No problem. If you have any more questions, just ask. – MauriceNino Aug 29 '19 at 14:23
  • The only thing I don't see accounted for is if the user is logged out for some server reason: thus I'm looking for something that forces reload of every page regardless. Frankly the site is academic, and the pages are quite lightweight in data, so server load is not an issue if the user's browser was forced to reload any page on the domain. – jbrehr Aug 29 '19 at 14:48
  • What do you mean? If you want to just reload the page on every "back-button click", just use Step #2 of the first solution and remove the `&& loginStateChanged` from the if statement @jbrehr – MauriceNino Aug 29 '19 at 19:21
  • In my description I put "etc" after "backbutton" to account for a variety of ways one might access a page in the cache, but to be more explicit: I am looking for a hard refresh every time anyone visits any page, regardless of logged in/out. This would solve every issue: the small payload for each page, and low site traffic on the server, will pose no problem for users or servers. – jbrehr Aug 29 '19 at 23:08
  • @jbrehr As far as you described it, my solution works exactly how you want it. Check out Solution 3) – MauriceNino Aug 31 '19 at 00:21
  • Solution 3 sends the browser into an unending reload loop - when the page loads, it triggers a reload, which triggers a reload... :) – jbrehr Aug 31 '19 at 08:24
  • @jbrehr can you try Solution 3 now? – MauriceNino Sep 01 '19 at 09:23
  • I cleared history, cache and cookies, everything before experimenting. Alas...it doesn't force reload. But if I log in and the hit the back button it doesn't reload from server - it shows the cached (non logged in) page. Same if I visit a page in history that has the new script. Sorry if this is frustrating...I don't know if what I am asking is possible. I tested what Amazon does and even they don't force a reload - I still look logged in if I hit the back button. – jbrehr Sep 01 '19 at 13:30
  • Ok I am very sorry, that I didnt try it out. Check that solution 3 again, it works 100%. I have tested it and linked the test files! – MauriceNino Sep 02 '19 at 20:47
2

@MauriceNino had already covered almost all the technical methods, though there is one more that exist in certain scenarios.

Not the best solution but if all your pages are created with certain dynamic url with parameters, you can just add a new parameter "v" or "version" = random login session key.

To make this happen, all and any URL's (that you don't want to cache) should be concatenated with above parameters. Create a centralized method to call ajax or pages and add this version parameter. Every time you login a new unique parameter will be created. Browser will always hit for a new request if the parameter is changed.

Though the down side to this is that your URL's won't be pretty. All your URL's would have to go through a filter (which i guess you would already have)

Prajyot Tote
  • 670
  • 5
  • 12
2

The best source I found for your problem was the following blog article that explains caching in a way that even I could understand: https://jakearchibald.com/2016/caching-best-practices/

TLDR: Don't try to fix this on the client side, let the server take care of this. Add the following response header on the server side:

Cache-Control: no-cache
Arne Klein
  • 662
  • 5
  • 12