Search code examples
google-chromenginxcachinghttp-headers

How to prevent Chrome from using disk cache when the assets change?


Setup

I have a static site served by Nginx. I use Webpack to build the assets with a contenthash in the filename:

index.html
main.723f2b08bd448896ca78.js
main.18a850dffbe46cca9feb.css

When I edit my JavaScript code and redeploy the site, the root directory changes to:

index.html
main.08ddcf9a702a6772ce2d.js # <- new hash
main.18a850dffbe46cca9feb.css

Problem

When I open a new tab in Chrome, type my domain name, and press Enter - in the Network tab, I see that Chrome is still loading the old files:

Name                           Status  Type        Initiator  Size          Time
mydomain.com                   200     document    Other      (disk cache)   4 ms
main.723f2b08bd448896ca78.js   200     script      (index)    (disk cache)  17 ms
main.18a850dffbe46cca9feb.css  200     stylesheet  (index)    (disk cache)  13 ms

When I click example.com, the HTML in Response includes the old script:

<script src="/main.723f2b08bd448896ca78.js"></script

Also, the Response Headers on index.html are outdated:

Content-Encoding: gzip
Content-Type: text/html
Date: Thu, 26 Jan 2023 09:21:31 GMT
ETag: W/"63d06108-202"
Last-Modified: Tue, 24 Jan 2023 22:51:52 GMT
X-DNS-Prefetch-Control: off

Furthermore, when I click on main.723f2b08bd448896ca78.js, the Response Headers are also out-of-date:

Accept-Ranges: bytes
Content-Length: 605458
Content-Type: application/javascript
Date: Thu, 26 Jan 2023 09:21:31 GMT
ETag: "63d06108-93d12"
Last-Modified: Tue, 24 Jan 2023 22:51:52 GMT

If I reload the page, Chrome loads the new files correctly. But if I then open a new tab again, type the domain, and press Enter - it still loads the old assets! This leaves me with a hard page reload (Ctrl + Shift + R) which finally resets the cache.

Expectation

When I load my site in Incognito I get the correct files as expected:

Name                            Status  Type        Initiator  Size        Time
mydomain.com                    200     document    Other      557 B       634 ms
main.08ddcf9a702a6772ce2d.js    200     script      (index)    606 kB      1.28 s
main.18a850dffbe46cca9feb.css   200     stylesheet  (index)    87.2 kB     497 ms

index.html Response:

<script src="/main.08ddcf9a702a6772ce2d.js"></script

index.html Response Headers:

Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html
Date: Thu, 26 Jan 2023 10:33:44 GMT
ETag: W/"63d249c7-202"
Last-Modified: Thu, 26 Jan 2023 09:37:11 GMT
Transfer-Encoding: chunked

main.08ddcf9a702a6772ce2d.js Response Headers:

Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 605595
Content-Type: application/javascript
Date: Thu, 26 Jan 2023 10:33:44 GMT
ETag: "63d249c7-93d9b"
Last-Modified: Thu, 26 Jan 2023 09:37:11 GMT

Question

I don't understand what I'm doing wrong - Nginx is returning new values for Etag and Last-Modified. The contenthash changes between deployments. Yet the JS file is still cached very aggressively. This problem happens to me and other people using the site. In this case, it caused a white screen of death, and I had to tell everyone to do a hard page reload (which is foreign and bewildering to the end users).

Is there a solution for this? I don't mind disk cache but how do I tell Chrome not to use it when the asset file changes? Is this an Nginx misconfiguration?

Thank you.


Solution

  • It's your job to tell the browser how long it can cache a resource. You do so by setting the Cache-Control response header (MDN). If you don't set it, the browser is free to decide for itself how long is appropriate. That seems to be what's happening in your case.

    Simply set Cache-Control: no-cache on your index.html response header and this problem will go away. The page will still be cached by the browser, but the browser won't use it without first checking with the server to see if it's still current (that's what the ETag is for).

    For your static resources, though, you should set a long cache time, since the whole purpose of versioning file names like that is to allow any given one to be immutable.