Using Immutable Caching To Speed Up The Web

When Firefox 49 shipped it contained the Cache-Control: Immutable feature to allow websites to hint which HTTP resources would never change. At almost the same time, Facebook began deploying the server side of this change widely. They use a URI versioning development model which works very well with immutable. This has made a significant impact on the performance of Facebook reloads with Firefox. It looks like other content providers will adopt it as well.

The benefits of immutable mean that when a page is refreshed, which is an extremely common social media scenario, elements that were previously marked immutable with an HTTP response header do not have to be revalidated with the server. Lacking this hint, the browser needs to guess which objects may or may not change on reload – wasting time on one hand or risking website incompatibility on the other.

For smaller objects, the work of this revalidation via a 304 HTTP response code can be almost as much work as just transferring the response fully.

It turns out this can save a lot of work. The page’s javascript, fonts, and stylesheets do not change between reloads. More importantly, the dozens of images do not change either – different images may be included by the markup, but the content of individual images do not change. Indeed, about the only thing that might change is the markup itself.

For Firefox users reloading Facebook content this has been a tremendous boon – the fastest request is one that is never made, and that is exactly what happens over and over again when refreshing a Facebook page. In my testing a typical feed may initially be comprised of 150 different resources. Pressing refresh in Firefox 49 generates just 25 network requests.


As you might imagine, this radically speeds things up. In my testing, it can often cut page reloading time in half. Facebook was also an early adopter of the brotli compression encoding. They use that to reduce the bandwidth usage of the dynamic markup, which cannot be cached, saving around 20% of the bytes transferred when compared to the older gzip standard. Brotli has been available in Firefox since Firefox 44.

Facebook’s servers are big winners too of course – a request never made saves bandwidth and CPU utilization which can in turn be spent on making the site faster for other requests.

“This change effectively eliminated revalidation requests to us from up-to-date versions of Firefox which, in many cases, can improve load times by seconds.” – Nathan Schloss, Software Engineer, Facebook




Facebook has been a great partner in this effort. Lately I’ve been spreading the word about immutable and other developers are adopting it too.

The BBC has picked it up on a trial basis:

Anecdotally, BBC sees reload times improve up to almost 50%, and finds 90% of requests are optimized away by immutable.

Implementations as future-looking as the InterPlanetary File System are interested too:

Also, products as venerable as the Squid proxy:


This has enough experience in the wild now to heartily recommend its use. To ensure adequate documentation it has also been adopted into the IETF on the Standards Track. All you need is a proper caching header to get started with your development.

About Patrick McManus

Principal Engineer at Mozilla focused on Platform Networking

More articles by Patrick McManus…


  1. Jeff R.

    “just 25 network requests”

    You know something is wrong when a statement like this is said with the expectation of sounding impressive.

    January 26th, 2017 at 10:43

    1. Jay

      Images are always separate network requests. Seeing as the most unique content on Facebook tends to be user-uploaded content (and ad content), I’d be careful judging things too soon

      February 9th, 2017 at 10:01

  2. Gerd Neumann

    Anybody wanting to read the Squid proxy image:

    January 26th, 2017 at 11:42

  3. Jeremie

    I’m glad we’re tackling the over-fetching of resources and the slowdown it causes when loading up pages.

    “Expires:” was supposed to do just that “immutable” is described in this article. But somehow neither Mozilla nor other browsers are respecting the expiry date and keep revalidating the resources despite having a date in the future.
    See RFC:

    It sounds from what you wrote that it was a design choice so that website poorly written would not suffer issues with incorrect configuration of the expiration date.

    After “expires”, “Cache-control: max-age” was added. It served the same purpose but with more granular functionalities. See RFC:
    . Again the browsers started to disregard the cache when refreshing pages in order to circumvent bad website configuration of the cache-control header.

    Is immutable going to suffer the same fate as the previous two attempts?
    Should we see a “really-immutable” flag sometime in the future? :-)

    If websites start to abuse the new value and things start to break as a resource of stale resources, will Mozilla (and other web browsers) start to revalidate the resources that had already been said to be immutable?

    January 26th, 2017 at 12:58

    1. Patrick McManus

      please see the difference in the below comment about disambiguating fresh from current

      January 27th, 2017 at 04:49

  4. Joshua Sinkfield

    I think any form of caching is a good way to stay reprovable on the internet.

    January 26th, 2017 at 14:48

  5. Jason Brown

    Bloody marvellous!

    Facebook is a huge data drain for those of us in third world countries, or in first world countries with third world internet (take a bow New Zealand) !

    Our Pacific Islands charge some of the highest internet rates in the world, while suffering some of the lowest pay rates – at least among those lucky enough to get pay at all.

    As in most countries, Facebook is hugely popular in the islands, given the importance of family first, friends second and ‘face-toface’ communications third. So data savings of 50% would make a huge difference to people there.

    Do you know if this cache feature is deployed worldwide, or, as usual, first-world first, third-world last?

    January 26th, 2017 at 15:58

  6. Omega

    This is overridden with CTRL+F5, yes?

    January 26th, 2017 at 17:17

    1. Patrick McManus

      yes, that style of ‘hard reload’ is used to fix corruption. We never did revalidation under those circumstances (because you often just revalidate the corruption if the etag part is right – so you do a full non conditional reload instead) so its behavior is unchanged.

      January 27th, 2017 at 04:48

  7. Sjon Hortensius

    Can you explain the difference compared to the existing `Cache-Control: public` without must-revalidate? It seems to me that combined with a high max-age, the effect should be the same.

    January 27th, 2017 at 00:34

    1. Patrick McManus

      The core concept is that without immutable the concepts of fresh and current are not clearly separated. http caching allows for fresh (i.e. cache replayable) data that isn’t the most current representation of a resource – and of course firefox normally uses that. But when you press refresh we in the past have revalidated even fresh things (i.e. with a high max age) to check to see if they were still the most current. immutable lets the cache know that there is never more than 1 version of that resource, so we only need to worry about freshness and never re-validate under those circumstances.

      January 27th, 2017 at 04:47

      1. Maarten Scholder

        But a max-age of 1 year (for example) should be close enough to being immutable. No sane site would use that for resources that actually do change.

        Imagine how a support department is going to explain that the site looks messy but that you expect it to be fixed in at most 1 year. Or think of the way you as a developer would explain it to your own support team. Or what happens if another change is made during that year.. etc. etc.

        Immutable is explicit and that has some merit, but long max-age without fuzzy magic heuristics should already do the trick. Immutable might look like KISS, but wouldn’t it be even simpler if max-age was just respected? Also, less random code.

        January 27th, 2017 at 05:23

      2. Gerd Neumann

        > But when you press refresh we in the past have revalidated even fresh things (i.e. with a high max age) to check to see if they were still the most current.

        Why? That’s also the point Jeremie is making above. If there’s a `Cache-Control: public` without must-revalidate but with high max-age, then Firefox should not check again. (Serious question because that’s how I always configured my servers)

        January 27th, 2017 at 05:42

  8. Anchal

    Hi Patrick,
    Very well written. Your article explains about the benefits of using immutable caching for improving the webpage reload time. Could you also help me to understand how to implement this in the code base.

    All help is appreciated.


    January 27th, 2017 at 00:49

  9. Justin Avery

    Amazing work Moz://a! The faster we can make the web the better.

    Out of interest are you aware of any articles that are covering the implementation method required to achieve immutable? I’m also unclear the exact reference meaning for ‘immutable’ and ‘never change’… is it absolutely literal?

    CSS and JS will never change until they need to change. At this point can we approach the revalidation through styles.css?v=0.1 -> styles.css?v=0.2 ? Or will it require styles0.1.css -> styles0.2.css to refresh the file (or does that all depend on the server setup?)

    Thanks for your patience on the basic questions, but I want to make sure I understand the full ramifications of the approach.

    January 27th, 2017 at 01:59

    1. Patrick McManus

      a change in query string should be sufficient to have a new url key in the cache

      January 27th, 2017 at 04:44

  10. Jason Grigsby

    What is the advantage of this versus the old YSlow guideline of setting far future expires?

    If you set far future expires for 50 years into the future, does setting Cache Control: Immutable provide any additional benefit?

    January 30th, 2017 at 10:49

    1. Jason Grigsby

      My co-worker pointed out that I missed comments above that answered this question. This is one time I should have read the comments. Sorry about the duplicate question.

      January 30th, 2017 at 12:32

  11. Wellington Torrejais

    I’m excited about this new option.
    And anxious to this in the next time.


    February 2nd, 2017 at 15:16

  12. Tara Li

    So – if your company decides to touch up the logo for your website, you *must* give it a new filename, and make the appropriate changes in all of the dependent code, instead of just letting it time out in caches normally? (Seriously – a 1 year cache validity message? People seriously underestimate the volatility of stuff on the Internet… I can’t see any *good* reason to set the cache time-out for more than a month…)

    February 4th, 2017 at 10:40

    1. Jeremie M

      @Tara: it means that if your company is unable to keep track of its resources to update them and you care about them updating in a timely manner, they you should not use headers meant for caching.

      You’d be better off just using etag, which provides the revalidation based on content and not timestamp.

      Here we’re talking of improving caching for website who do keep track of their resources correctly. And the problem with older flags is that they have been misused by developers leading to the flags being now ignored, hence the new for yet-another-caching flag :)

      February 5th, 2017 at 10:49

      1. Tara Li

        So, what’s the plan for the next header, after this one gets so abused and ignored?

        February 18th, 2017 at 10:49

        1. Patrick McManus

          fwiw I disagree that this has anything to do with old attributes being ignored. They always failed to distinguish between fresh and current – immutable allows that.

          February 18th, 2017 at 14:21

  13. Ryan

    Why does this only work for https and not http?

    February 6th, 2017 at 13:25

  14. Cory

    Is there any benefit to adding the immutable cache header for assets which include a unique hash in their filename and are served with a max-age of 1 year?

    On the app that I work on, all assets are hashed and the hash is included in the filename. When requested, the response is always 200 and served from cache. Would an immutable cache header improve this any further, or is this meant to optimize static assets with static filenames and low max-age values?

    February 7th, 2017 at 16:57

    1. Patrick McManus

      yes – immutable lets you distinguish between just fresh (max-age) and fresh and current (max-age plus immutable). If the resource is known current you don’t need to revalidate it on reload.

      note that reload (the reload icon, or shift r) is not the same as simply reuse – for which fresh is sufficient. This is all about explicit reload.

      February 8th, 2017 at 05:22

      1. Cory

        Ahh good to know. Thanks for the quick response.

        I wonder if this might help speed up out automated tests by serving content a bit more quickly. Im not sure what kind of refreshing goes on between each of selenium’s tests.

        February 8th, 2017 at 08:13

  15. Yo

    Seems to me that partnering with Facebook isn’t such a great thing these days in terms of “privacy.” It’s become too popular, and Firefox seems to start becoming too popular, too. But I hope I’m wrong.

    February 8th, 2017 at 12:34

Comments are closed for this article.