Cache static file as much as you can

Jun 01, 2015

The browser sends http header: If-Modified-Since and If-None-Match to check whehter a static file has been updated when it fetches the file again. If the file has not been updated, the server would send 304 Not Modified HTTP header message and the browser would use cached version instead of downloading the file again. This is the default cache mechanism of most web servers, however there is some room for improvement in the web performance. The default cache behavior still requires establishing HTTP connection between the client and the server in order to check whether the file has been modified. It may takes the browser tens to hundreds of milliseconds to talk to the server and it also consumes server resources.

First access to a website:

Second access to a website:

From the pictures above, it still took the browser nearly 1 second to load all resources in second access though all responses were 304 Not Modified, we can see that each 304 connection took around 150ms.

We can force the browser to use cached version by specifying an expiration date or a max-age value, hence the browser won't talk to the server anymore.

Cache-Control & Expires

You can set "Cache-Control" header with "max-age" value (in seconds) to control the cache lifecycle.

Cache-Control: max-age=3153600

You can set "Expires" header with the specific cache expiration date as well.

Expires: Fri, 01 Jul 2016 00:00:00 GMT

Either using "Cache-Control" or "Expires" will significantly increase the page load speed, the browser doesn't send static file requests to the server anymore.

What if file is updated?

The disadvantage of this ultimate cache mechanism is obvious: the browser will always use the cached version even if the file has been updated.

The browser determines whether to use the cached version by the file URL, you just need to ensure the file URL is changed as well when you update the file, hence the browser will re-download the file from the server. Certainly you must be sure that you didn't set a long cache expiration for the HTML. Basically the file URL can be changed in following shapes.

  1. Generate a random file name when building the project
    <script type="text/javascript" src="../src/js/febfcddf6bc65078231ce9026c2b96ad.js"></script>
    
  2. Add version number to the file name
    <script type="text/javascript" src="../src/js/lib/jquery-1.11.3.min.js"></script>
    
  3. Add version number to the URL parameter
    <script type="text/javascript" src="../src/js/default.js?v=2"></script>
    

How to add Cache-Control?

IIS

  1. Select the file folder you want to add Cache-Control, double click HTTP Response Headers.

  2. Click Set Common Headers..., check Expire Web content and set the max-age value as you want.

  3. You can also edit the web.config as below.

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
     <system.webServer>
         <staticContent>
             <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
         </staticContent>
     </system.webServer>
    </configuration>
    

Apache

  1. Edit the .htaccess file and insert something like this:
    # 1 year for all your static assets
    <filesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
    Header set Cache-Control "max-age=31536000, public"
    </filesMatch>
    

NodeJS

  • For Express Static Middleware Cache-Control, the max-age should be set in milliseconds.

    var oneYear = 60 * 1000 * 60 * 24 * 365;
    app.use(express.static('staticFile', { maxAge: oneYear }));
    
  • For native NodeJS, you can use setHeader function to set the Cache-Control header.

    response.setHeader('Cache-Control', 'max-age=31536000');