5

When using external js files, browsers can be forced to reload the files. See here.

Recently, I've found out that INLINE scripts are also cached, at least in Chrome, version 80.0.3987.132, example of snippet:

<html>
    <head>
        <script>alert("I am cached!");</script>
    </head>

    <body>
        <script>alert("Me too!");</script>
    </body>
</html>

What's the way of refreshing inline scripts?


Update 1: I do have to mention that the webserver returning the content is using HTTP 2.0

Update 2: A solution that works is to have an auxiliary script as base and when the page loads get the "real" script content through ajax or websocket then append it to head like so:

function addScript(content){
    let s = document.createElement('script');
    s.innerHTML = content;
    document.head.appendChild(s);
}

This does the job but its not optimal as it needs more requests than necessary.

Update 3: Headers sent from backend neither seem to work, using these headers:

Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
Header().Set("Pragma", "no-cache") // HTTP 1.0.
Header().Set("Expires", "0") // Proxies.

Update 4: As per Jinxmcg's answer, the doc https://v8.dev/blog/code-caching-for-devs Don’t change URLs mentions:

we may one day decide to associate caches with the source text rather than source URL, and this advice will no longer be valid.

Probably that day has come and is also applied to inline scripts.


Thank you everyone for participating

Final Solution (works at least under my circumstances):

1 Backend headers:

w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0") // HTTP 1.1.
w.Header().Set("Pragma", "no-cache") // HTTP 1.0.
w.Header().Set("Expires", "0") // Proxies.

2 Random string in HTML, JS and CSS, example:

<html>
    <head>
        <style>
            --cache-color: #8528cc; //Random hex color generated by backend
        </style>
        <script>
            console.log("<?php echo date(); ?>");
            alert("I am cached!");
        </script>
    </head>

    <body>
        <div>Hidden DIV with a random value: <?php echo date(); ?></div>
        <script>
            console.log("<?php echo date(); ?>");
            alert("Me too!");
        </script>
    </body>
</html>
Alpha2k
  • 2,031
  • 6
  • 31
  • 58
  • @JonSkeet got any idea how to handle this? :P – Alpha2k Mar 17 '20 at 18:37
  • 2
    Your example will always say "I am cached!" and "Me too" regardless of cache. Can you post a relevant example? – Jinxmcg Mar 18 '20 at 10:08
  • @Jinxmcg yeah because the script has to change in backend (web server) but next index view the script must come updated. Cannot emulate client and server in order to provide example. – Alpha2k Mar 20 '20 at 09:25
  • 1
    the test would be to append a random number to the strings and check if it changes between reloads and add it in the HTML too and see if it changes. As I written in my answer I highly doubt you're hitting a inline js cache, I think a full page cache it is what you hit and you can fix that with appropriate caching headers. – Jinxmcg Mar 20 '20 at 10:00
  • @Jinxmcg tried with headers, chrome still does cache... damn it – Alpha2k Mar 20 '20 at 10:05
  • But it is a full HTML page (including JS script) cache or not? However, because you use http2 you can PUSH a separate JS file to the client in the initial request, and the browser will get the JS file from the PUSH section and will not make a separate request. So 1 request and client gets both HTML (cachable, and fresh JS). Look into http2 push. – Jinxmcg Mar 20 '20 at 20:11
  • @Jinxmcg yes, the webserver returns a complete page, with HTML, CSS and JS all together. There are no separate file requests for css and js. No matter what I change, HTML, CSS or JS, the cache is still persistent... Every time the backend server restarts it will respond with new HTML, JS and CSS but (in this case chrome) the cache of the older content is still there despite the fact that the webserver DOES send the new content... – Alpha2k Mar 21 '20 at 12:31
  • 1
    Ok. This seems to be Server caching rather than Browser caching. Can you few cURL tests? and see if the content changes between cURL requests? because it might be that the server it is actually serving the same content from a local cache. – Jinxmcg Mar 21 '20 at 15:43
  • @Jinxmcg sorry to dissapoint you, even tought CURL returns different content length, chrome still does cache older version... only doing CTRL + F5 solves it, normal refresh doesn't. – Alpha2k Mar 21 '20 at 22:38
  • 1
    Do not worry, I am not dissapointed :) I was just trying to eliminate all the possible causes for this cache. However if you want me to help you further you need to detail a little bit: Are you using any framework, javascript framework? Does the webpage create any workers becase they can act as cache, is it PWA etc...nodejs static app? Do you have a front CDN like cloudflare? – Jinxmcg Mar 22 '20 at 11:18
  • @Jinxmcg No frameworks, neither in backend nor in front (not even jquery). The backend is made in GO and its just a plain webserver answering on port 80. No CDN, I'm on localhost only. – Alpha2k Mar 22 '20 at 15:43
  • @alpha2k you can put your entire scripts inside the click Event listener of a hidden element , then create a function that when you call it , it triggers the onClick event of that element and whole your scripts would be executed again – BrightFaith Mar 24 '20 at 10:41
  • Can you give us your use case of what you are trying to do? Do you want to see the updates to your script when you are debugging? Or do you update your javascript in production so often that you don't want your visitors' browser to cache the script? – Enrico Mar 24 '20 at 11:22

3 Answers3

7

I think the browser caches the inline javascript only when the page is opened for subsequent calls in that session and does not keep it after you close or refresh the page.

However, this means that the browser gets the HTML (including JS) from its cache in your case. Therefore you could try sending some headers along with your page that force the browser not to use it's cached HTML copy and use the new html+js.

In order to test if it is a HTML cache or "inline JS" cache issue, make your html dynamically change and confirm that it is changing on refresh but the inline JS execution does not.

You can find more details regarding js cache here: https://v8.dev/blog/code-caching-for-devs

Jinxmcg
  • 954
  • 9
  • 18
1

Using document.createElement('script') to "refresh" your scripts is bad solution. Probably something else is wrong with your caching.

Have you tried Cache-Control: "no-store, no-cache, must-revalidate, post-check=0, pre-check=0" ? https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

If you are using proxies check out proxy-revalidate.

When debugging you could try shift + F5 or holding shift while pressing the reload button to force a complete refresh in Google Chrome. (if you have changed your script)

Enrico
  • 1,202
  • 1
  • 11
  • 19
1

Consider using the Cache-Control directive of max-age for your web page(s).

Normally, max-age might be set to a relatively long amount of time in seconds. This is done to improve performance by having the client frequently reuse cached files before they are refreshed.

Prior to releasing a change that you want the client to refresh immediately, drop the max-age value down to zero or a few seconds. Then, wait for the original max-age time duration to expire so that all active clients are updated with the new max-age value.

Once this wait period passes, push the file update(s) and revert to the original and longer max-age value.

This sequence will force the desired file change to be refreshed by the clients.

JohnH
  • 1,646
  • 1
  • 22
  • 27
  • The Cache-Control is set, its a header sent by backend: "Cache-Control", "no-cache, no-store, must-revalidate", doesn't seem to work, probably because i'm using HTTP 2.0, not sure tough or maybe there is a cache-control for HTTP 2.0 specific, however I was unable to find anything related. PD: oh, will try max-age 0 too – Alpha2k Mar 24 '20 at 14:35
  • The no-store Cache-Control directive excludes the use of expiration directives like max-age. Using both may result in an error condition that causes different behaviors on different clients. The no-store directive should cause clients to always refresh their content because caching is disabled by this directive. The no-store directive sacrifices download performance for always fresh content. – JohnH Mar 24 '20 at 15:23