Search code examples
angularexpressbrowser-cache

Angular/Express app is looking for old javascript files in Chrome


I have an Angular web app, served by an Express server. I have found that when I update my app and deploy it (to Google App Engine), and I refresh the browser, I sometimes get a blank page and this error:

main.f2b54282bab6f51a.js:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

Output hashing is enabled in Angular, as you can see from the main.js filename, and the filename listed in the error is one from the previous deployment. So it appears that the browser is using an outdated, cached javascript file.

Doing a "Empty Cache and Hard Reload" in Chrome fixes the page, which also suggests this is a problem with caching. (This error also happens in Firefox. I haven't yet seen it in Safari.)

I thought maybe the browser was caching the index.html page, so I tried to disable caching of the app in Express:

 // serve angular app                                            
 let options = {                                                 
   setHeaders: (res, path, stat) => {                            
     res.set({                                                   
       'Cache-Control': 'no-store',                              
       'Expires': 0,                                             
     });                                                         
   }                                                             
 };                                                              
 app.use(express.static(__dirname + '/dist/dashboard', options));                                                                 

I can verify that those headers are correctly set when the app is served, by using curl -v, but the problem still happens.

Is this actually a problem with caching, or is something else going on?


Solution

  • I currently am using this, which seems to be working:

     app.use((req, res, next) => {                                                                 
       let suffixes = ['.png', '.jpg', '.jpeg', '.svg', '.mp4', '.ico', '.json', '.js' ];          
       if (!suffixes.some(o => req.url.endsWith(o))) {                                             
         res.set('Cache-Control', 'no-cache, no-store, must-revalidate');                          
         res.set('Pragma', 'no-cache');                                                            
         res.set('Expires', '0');                                                                  
       }                                                                                           
       next();                                                                                     
     });                
    
     app.use(express.static(__dirname + '/dist/dashboard', options));                                                                 
    

    It sends the "don't cache this" headers only for the urls that correspond to angular routes; that way it will still cache the static files such as images that it makes more sense to cache.

    Note that I am still caching Javascript files, because Angular generates unique hashes in the names (e.g. main.ea9376f39d804178.js), so after a new deployment, the new file will be served.

    It's a good idea to look at the requests in the Network section of the Chrome debugger to see which requests are being cached (i.e. have status 304), to make sure it's doing what you expect.