Caching workflow between clients and servers

While as frontend developers we seldom do anything in regards to caching, as a full-stack developer you need a good understanding of how caching works in the browser and on the server. Before embarking on a full-stack path, the only time I needed to do anything related to caching was when I wanted a new version of a script or stylesheet to be picked up by users, which I did by adding a query parameter to the end of the path:

<link rel="stylesheet" href="style.css?v=1.2.3">

This invalidates browser's cache, forcing browser to fetch a new version of the resource from the server. But what happens if the resource path doesn't change? Then a set of headers in request and response objects define how caching works. These headers are Cache-Control and ETag on response object and If-None-Match on request object.

The first time a browser requests a resource, the server returns a response with 200 status. Server generates a unique hash from response body and sends it in a ETag header. Below is an example of a response with corresponding headers:

// response
Cache-Control: public, max-age=0, must-revalidate
Etag: "a6254f652913ca53429b315c9167b3d2"

Since max-age is set to 0 seconds, browsers consider the resource as stale in future requests and use the value of ETag in a If-None-Match header to send a so-called conditional request to the server:

// request 
If-None-Match: "a6254f652913ca53429b315c9167b3d2"

When server receives such request it checks whether the ETag it has for the current version of the resource matches the value of ETag sent in If-None-Match header and if it does, server sends a response with 304 status and no body, telling the browser it can use the resource from the cache. Note that ETag header is not sent in 304 responses.

Related Links: