DEV Community

Cover image for Cache-Control header & Cache-Busting
Cornelia
Cornelia

Posted on

Cache-Control header & Cache-Busting

A while ago, I found a bug in one of my company’s earliest PWAs; some users’ devices did not fully receive the latest update. Investigating this issue led me down the rabbit hole of cache-busting and caching strategies.
This article focuses on the Cache-Control Header and possible cache-busting strategies. I might publish a second part, that will focus on caching with service workers.

Cache-Control Header

The Cache-Control header provides the ability to manage the cache per asset. It can have one or multiple directives.

public

This directive allows any cache to store the resource (incl. browser caches and CDNs). Resources that do not include user-specific data could be cached using this directive.
If a resource specifies other directives, but neither public nor private, public is set implicitly.

Cache-Control: public
Enter fullscreen mode Exit fullscreen mode

private

This directive indicates that only a browser can cache the resource. private is useful for responses that are unique to each user.

Cache-Control: private
Enter fullscreen mode Exit fullscreen mode

no-store

If this directive is set, no cache will store the resource. Use this directive for resources that include sensitive or time-critical information.

Cache-Control: no-store
Enter fullscreen mode Exit fullscreen mode

no-cache

Caches will always re-validate resources that have this directive.
The cache will ask the server for a newer version. If it has one, it replies with a 200 and the new file to download and use. If the cached version is the latest one, it responds with 304. The cache then serves the asset it already has.
Since no-cache will always trigger a re-validation, it cannot be combined with the max-age directive.

Cache-Control: no-cache
Enter fullscreen mode Exit fullscreen mode

max-age

This directive sets a time (in seconds) within the asset is safe to use. After that time has passed, the file will be considered stale. Stale files will be re-validated the next time the client requests the file and restarts the max-age. Note that caches might eject stale files. In that case the client will need to download the resource again.
If the user explicitly refreshes the page, the browser will re-validate the resource even though the max-age hasn’t passed yet.

Cache-Control: max-age=60
Enter fullscreen mode Exit fullscreen mode

s-maxage

This directive overwrites the max-age directive for shared caches (like proxy servers or CDNs).

Cache-Control: max-age=86400, s-maxage=3600
Enter fullscreen mode Exit fullscreen mode

must-revalidate

Use this directive in combination with max-age.
must-revalidate prevents caches from ejecting the resource.
Again, refreshing the page will cause re-validation even if the specified time hasn’t passed yet.

Cache-Control: must-revalidate, max-age=600
Enter fullscreen mode Exit fullscreen mode

proxy-revalidate

This directive overwrites the must-revalidate directive for public caches.

stale-while-revalidate

This directive allows caches to serve the stale file while re-validating the resource for a specified amount of time (in seconds).

Cache-Control: max-age=31536000, stale-while-revalidate=86400
Enter fullscreen mode Exit fullscreen mode

stale-if-error

If this directive is set, the cache will serve the stale resource for the specified amount of seconds if the server responds with a 5xx error upon re-validation.

Cache-Control: max-age=2419200, stale-if-error=86400
Enter fullscreen mode Exit fullscreen mode

no-transform

This directive prevents intermediaries from modifying responses. For example, a telco provider might optimise images by default. You most likely want to prevent that because it would likely cause a loss in quality.
Since intermediaries are not allowed to modify responses on HTTPS connections, this directive will only affect HTTP connections.

Cache-Control: no-transform
Enter fullscreen mode Exit fullscreen mode

immutable

This directive specifies the amount of time within the resource will not change. During that time, caches will not re-validate it, not even on manual page refreshes, nor download newer versions. So, ensure a good cache-busting (see below) strategy before using the immutable directive!

Cache-Control: max-age=31536000, immutable
Enter fullscreen mode Exit fullscreen mode

Cache-busting

Cache-busting strategies define how cached resources can be updated or replaced. These are some strategies that you can use:

Fingerprinting filenames

A random hash is added to directories, per example: images-rzt87032ztr. This is usually easier to set up but could cause a lot of unnecessary traffic. Caches will need to download all resources within the folder, even though only a couple might have changed.

Query String

A specific version is requested via parameter, per example: style.css?v=1.0.0. This provides cache-busting per individual file but might still be an insufficient solution since many proxy servers and CDNs omit query strings while serving from the cache. Therefore these caches cannot be busted using this strategy.

Conclusion

What you should take away:
Work out a cache-busting strategy first, then define a caching strategy.


Thanks for reading.

Top comments (1)

Collapse
 
pdfreviewer profile image
pdfreviewer • Edited

If you're using caddy, add following lines to the config file (usually /etc/caddy/Caddyfile) to enable Cache-Control for all requests:

:80 {
    ...
    header {
        Cache-Control "max-age=86400"
    }
    ...
}
Enter fullscreen mode Exit fullscreen mode

or to enable Cache-Control for specific request /css/style.css

:80 {
    ...
    header /css/style.css {
        Cache-Control "max-age=86400"
    }
    ...
}
Enter fullscreen mode Exit fullscreen mode