Chrome not caching Webpack dynamically imported javascript chunks regardless of cache-control header; Firefox caches

Viewed 377

I have one entry pack in my homepage that after DOMContentLoaded uses import() to dynamically import a heavy JS chunk.

Watching DevTools in the network tab, I see that the behavior is exactly as expected: after DOMContentLoaded, a new request is made to load that separate chunk.

However, I noticed that while all other chunks (the initial sync ones, that are loaded imediatelly) are correctly cached (their status is a grayed out 200, with a size memory_cache), the dynamically imported chunks ALWAYS gets requested from the server and, worse, it's re-downloaded with a 200 status, even tough it's content hasn't changed.

enter image description here

This doesn't happen in Firefox at all; the dynamic imports are cached as well.

enter image description here

This happens with all dynamic imports, regardless of page or entry pack.

Inspecting the response headers in Chrome for those dynamic import assets, you can clearly see it was supposed to be cached:

cache-control: max-age=315360000
cache-control: public
content-encoding: gzip
content-length: 210215
content-type: application/javascript
date: Sat, 15 May 2021 19:33:45 GMT
expires: Thu, 31 Dec 2037 23:55:55 GMT
last-modified: Sat, 15 May 2021 19:32:27 GMT
pragma: public
server: nginx
vary: Accept-Encoding

This is a Rails 6 with Webpack 5 gem setup, so all packs are served from /packs/. This is the NGINX config:

location ~ ^/(assets|packs|images|javascripts|stylesheets|swfs|system|uploads|blog-media)/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
    add_header Pragma public;
    add_header ETag "";
    break;
  }
  • I have 'disable cache' NOT selected in my Chrome Dev Tools;
  • The last-modified response header is stable (it's not changing);
  • The filename is stable (it's not changing);
  • Content-length is not changing;
  • I have used diffchecker.com to compare, line by line, both the REQUEST and RESPONSE headers of chunks that are being cached and the dynamic chunks that are not... there's literally no difference but for the obvious path and content-length fields;

It's also relevant to note that, in Firefox, if I reload the page with CMD R, it automatically adds Cache-Control max-age=0 to the request headers, but that makes nginx return 304 for all chunks (so, the requests hit the server and appear in NGINX log, but nothing is download because of 304 responses and in cache appears in the transfered column. But if I click on my logo (which acts as a navigation to the homepage), then I see the 200 status, in cache in transfered column, and no requests hit nginx at all. Everything as expected.

Chrome, however, acts totally different. A CMD R doesn't add that cache-control max-age 0 header, so all chunks (with the exception of the dynamic imports) return 200 and (memory cache) in the size column. These don't hit nginx. However, the dynamic imports requests not only hit nginx, but also NGINX returns 200 status, forcing the download, so I can't understand why NGINX is sending the asset again (200) instead of a 304 response as it happens with Firefox.

I went as far as customizing the nginx log to add cache=$http_cache_control to the output, but it's empty for the dynamic imports; (I can see firefox's max-age=0 when I reload the page as described above).

UPDATE This is so weird that it happens at random. Once every ~5 reloads with CMD+R, the dynamic chunks also appear as cached as expected:

enter image description here

Maybe I hit a Chrome bug? (Chrome version 90.0.4430.212), since Firefox works as expected?

0 Answers
Related