Caching is a popular technique used in basic computer system design. And as the technology is evolving, the use of caching is increasing in almost all fields, especially if the resource fetching or computation is a costly affair. In the modern internet world, caching is used by all browsers to keep a local copy of resources, such as images, additional stylesheets, or JavaScript files used on a webpage. The reason is very simple, these resources do not change often and hence network roundtrip can be saved by storing and serving files locally.
Page Contents
When browsers fetch the main HTML document or additional resources linked with the webpage, all of these calls contain two main parts, the header and body content. Headers sent by the server tell the browser if the content should be cached or should not be. This header, ‘Cache-Control’ can have one or more values like ‘no-store’, ‘no-cache’, ‘private’, ‘public’, ‘must-revalidate’, ‘max-age’, etc. Based on these directives, a browser can cache or skip caching the resource.
If the resource can be cached, the next important question is, how long the resources can be cached? and how the browser will know if they are stale and should refresh them again?
This is determined by values in the ‘max-age’ and ‘s-maxage’ directives. Consider the below example where the header suggests a browser or similar client that content can be cached for the next 10 minutes (600 seconds) and calculated from the time of the response.
Cache-Control: max-age=600
If ‘max-age’ is having a 0 value, the content will not be cached. SImilar to ‘max-age’, another header ‘s-maxage’ is used for the same purpose but is shared in nature. ‘s-maxage’ can be used by CDN services to serve the cached content to multiple clients. This can be used for public content like a blog article, news content, library files etc.
Caching the content for a short period of time greatly improves a website visitor’s experience and helps save their bandwidth as the resources are not fetched again. However, once the cache is expired, the browser has to download the fresh content again and keep track of the next expiry. Conditionally, even before the expiry time, modern browsers can periodically validate the previously sent response headers with the server and try to check if headers are modified or fresh content is available for the URL which was previously cached. This looks absolutely unnecessary in those cases, where it is known that the content is never going to change.
As an alternative, HTTP header 304 can be used, but that too requires a connection between the client and the server for validation.
When it is known in advance that the content or header of a resource will never change in the future, the content can be theoretically cached forever and need not be validated ever in the future with the server. The below server response header is used for this purpose-
Cache-Control: public, max-age=31536000, immutable
In the above example, the server tells the browser that the content is immutable and can be cached forever. However, a ‘max-age’ value is also supplied for browsers that do not support the immutable feature yet. The ‘max-age’ time is of one year and really long for any practical purpose. Facebook has achieved ~60% savings with this tweak.
All of your website assets can be served with immutable cache header so they can be retailed on the client-side and CDN hopes for really long. But you need to be careful how they are referenced on the webpage. If incorrectly referenced, any changes made by you on the website may never reflect for your regular visitors.
Consider these three examples where an external JavaScript is included in a webpage in three different ways. The first one is a script file. The second and third one is also the same file, but with a different URL signature that points to the same file on the server-side but for the browser, they are different resources. When changes are made to the original file, instead of purging or invalidating the existing cache, a new version can be set in the URL and referenced in the main document. This will guarantee the delivery of the latest application code to the browsers without bothering about the older version if they are floating around some nodes.
See the below example which is used by jQuery CDN, and a similar pattern is used by almost all frameworks and libraries.
https://code.jquery.com/jquery-3.6.0.min.js
The below examples demonstrate how you can use the bursting pattern for your website’s assets. The first line is the usual way of including assets, the second and third line uses cache bursting pattern.
<script src="https://example.com/main.js"></script>
<script src="https://example.com/main.js?v=version-id"></script>
<script src="https://example.com/version-id/main.js"></script>
<link rel="stylesheet" href="https://example.com/style.css" />
<link rel="stylesheet" href="https://example.com/style.css?v=version-id" />
<link rel="stylesheet" href="https://example.com/version-id/style.css" />
<img src="https://example.com/images/my-hero.jpg" alt="" />
<img src="https://example.com/images/my-hero.jpg?v=version-id" alt="" />
<img src="https://example.com/images/version-id/my-hero.jpg" alt="" />
The above patterns can also be helpful if you are using some CDN service as it eliminates the need of purging the network every time you make changes. Most CDN providers including Rabbit Loader charges you only for the bandwidth consumed in serving the files, no matter what is the size of files stored at Points of Presence (PoPs) due to multiple iterative versions or copies of the same file.
Rabbit Loader has built-in support for immutable caching and can be controlled easily in the page rule for static content and by default, all assets are served with this pattern without site owners worrying about changing anything at their server end.
If you are using an apache web server, you can add these lines in the .htaccess file under the root directly containing your website’s index.html file. Make sure you also use version-ed aka cache buster URL pattern.
<filesMatch ".(png|jpg|gif|jpeg|woff|ttf|eot|otf|svg|ico|js|css)$">
Header set Cache-Control "public, max-age=315360000, immutable"
</filesMatch>
Add the below snippet in your NGINX configuration file
location ~* \.(png|jpg|gif|jpeg|woff|ttf|eot|otf|svg|ico|js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
header('cache-control: public, max-age=31536000, s-maxage=31536000, immutable', true);
At the time of writing this article, only Firefox, Safari, and Microsoft Edge browsers are supporting this feature. If you implement it, ~21.48% of the global users can start taking advantage of the feature. Looks like the Chrome team is still working to resolve a few issues around it and maybe rolling out soon.