Netlify
Leaving out the Cache-Control
response header does not disable HTTP caching! Instead, browsers effectively guess what type of caching behavior makes the most sense for a given type of content. Chances are you want more control than that offers, so take the time to configure your response headers.
There are two important scenarios that you should cover when configuring your web server's response headers.
Versioned URLs are a good practice because they make it easier to invalidate cached responses.
Cache-Control: max-age=31536000
) but your designer just made an emergency update that you need to roll out immediately. How do you notify browsers to update the "stale" cached copy of the file? You can't, at least not without changing the URL of the resource. After the browser caches the response, the cached version is used until it's no longer fresh, as determined by max-age
or expires
, or until it is evicted from the cache for some other reason—for example, the user clearing their browser cache. As a result, different users might end up using different versions of the file when the page is constructed: users who just fetched the resource use the new version, while users who cached an earlier (but still valid) copy use an older version of its response. How do you get the best of both worlds: client-side caching and quick updates? You change the URL of the resource and force the user to download the new response whenever its content changes. Typically, you do this by embedding a fingerprint of the file, or a version number, in its filename—for example, style.x234dff.css
.When responding to requests for URLs that contain "fingerprint" or versioning information, and whose contents are never meant to change, add Cache-Control: max-age=31536000
to your responses.
Setting this value tells the browser that when it needs to load the same URL anytime over the next one year (31,536,000 seconds; the maximum supported value), it can immediately use the value in the HTTP Cache, without having to make a network request to your web server at all. That's great—you've immediately gained the reliability and speed that comes from avoiding the network!
Build tools like webpack can automate the process of assigning hash fingerprints to your asset URLs.
You can also add the immutable
property to your Cache-Control
header as a further optimization, though it will be ignored in some browsers.
Unfortunately, not all of the URLs you load are versioned. Maybe you're not able to include a build step prior to deploying your web app, so you can't add hashes to your asset URLs. And every web application needs HTML files—those files are (almost!) never going to include versioning information, since no one will bother to use your web app if they need to remember that the URL to visit is https://example.com/index.34def12.html
. So what can you do for those URLs?
This is one scenario in which you need to admit defeat. HTTP caching alone isn't powerful enough to avoid the network completely. (Don't worry—you'll soon learn about service workers, which will provide the support we need to swing the battle back in your favor.) But there are a few steps you can take to make sure that network requests are as quick and efficient as possible.
The following Cache-Control
values can help you fine-tune where and how unversioned URLs are cached:
no-cache
. This instructs the browser that it must revalidate with the server every time before using a cached version of the URL.no-store
. This instructs the browser and other intermediate caches (like CDNs) to never store any version of the file.private
. Browsers can cache the file but intermediate caches cannot.public
. The response can be stored by any cache.Check out Appendix: Cache-Control
flowchart to visualize the process of deciding which Cache-Control
value(s) to use. Note also that Cache-Control
can accept a comma-separated list of directives. See Appendix: Cache-Control
examples.
Along with that, setting one of two additional response headers can also help: either ETag
or Last-Modified
. As mentioned in Response headers, ETag
and Last-Modified
both serve the same purpose: determining if the browser needs to re-download a cached file that has expired. ETag
is the recommended approach because it's more accurate.
ETag
header, are designed to solve. The server generates and returns an arbitrary token, which is typically a hash or some other fingerprint of the contents of the file. The browser doesn't need to know how the fingerprint is generated; it only needs to send it to the server on the next request. If the fingerprint is still the same, then the resource hasn't changed and the browser can skip the download.By setting ETag
or Last-Modified
, you'll end up making the revalidation request much more efficient. They end up triggering the If-Modified-Since
or If-None-Match
request headers that were mentioned in Request headers.
When a properly configured web server sees those incoming request headers, it can confirm whether the version of the resource that the browser already has in its HTTP Cache matches the latest version on the web server. If there's a match, then the server can respond with a 304 Not Modified
HTTP response, which is the equivalent of "Hey, keep using what you've already got!" There's very little data to transfer when sending this type of response, so it's usually much faster than having to actually send back a copy of the actual resource being requested.
/file
from the server and includes the If-None-Match
header to instruct the server to only return the full file if the ETag
of the file on the server doesn't match the browser's If-None-Match
value. In this case, the 2 values did match, so the server returns a 304 Not Modified
response with instructions on how much longer the file should be cached (Cache-Control: max-age=120
).The HTTP Cache is an effective way to improve load performance because it reduces unnecessary network requests. It's supported in all browsers and doesn't take too much work to set up.
The following Cache-Control
configurations are a good start:
Cache-Control: no-cache
for resources that should be revalidated with the server before every use.Cache-Control: no-store
for resources that should never be cached.Cache-Control: max-age=31536000
for versioned resources.And the ETag
or Last-Modified
header can help you revalidate expired cache resources more efficiently.
Try it! Try the HTTP Cache codelab to get hands-on experience with Cache-Control
and ETag
in Express.
If you're looking to go beyond the basics of using the Cache-Control
header, check out Jake Archibald's Caching best practices & max-age gotchas guide.
See Love your cache for guidance on how to optimize your cache usage for return visitors.
If you have more time, here are further ways that you can optimize your usage of the HTTP Cache:
stale-while-revalidate
directive if some degree of staleness is acceptable in your Cache-Control
policy.Cache-Control
flowchart #Cache-Control
examples #Cache-Control value | Explanation |
---|---|
max-age=86400 | The response can be cached by browsers and intermediary caches for up to 1 day (60 seconds x 60 minutes x 24 hours). |
private, max-age=600 | The response can be cached by the browser (but not intermediary caches) for up to 10 minutes (60 seconds x 10 minutes). |
public, max-age=31536000 | The response can be stored by any cache for 1 year. |
no-store | The response is not allowed to be cached and must be fetched in full on every request. |