Test case:
<script>
console.log('request');
(new Image()).src = 'https://s3.amazonaws.com/mapbox-gl-js/tests/no-cache.png';
setInterval(function() {
console.log('request');
(new Image()).src = 'https://s3.amazonaws.com/mapbox-gl-js/tests/no-cache.png';
}, 5000);
</script>
Neither Chrome nor Firefox make any network requests for no-cache.png
beyond the first, despite it being served with Cache-Control: no-cache
, indicating that user agents must revalidate cached content.
A few questions here (e.g. this one) touch on this and provide workarounds, but I'm most interested in answering more fundamental questions:
- What web specifications, if any, permit or require this behavior?
- If it is not specified, is it at least officially documented by one or more browsers?
- What controls, if any, do web authors have over this behavior?
- In particular, is there a way to bust the cache without losing the benefits of revalidation via
If-None-Match
, as the use of a cache-busting query parameter does?
The special behavior for images is specified in the HTML Standard:
Each Document
object must have a list of available images. Each image in this list is identified by a tuple consisting of an absolute URL, a CORS settings attribute mode, and, if the mode is not No CORS, an origin. Each image furthermore has an ignore higher-layer caching flag.
It is this flag that controls whether a user agent removes an entry from the list of available images or not given higher-layer caching semantics for the resource (e.g. the HTTP Cache-Control
response header).
The flag is set and the image added to the list of available images when the networking task to load an image source completes.
According to this comment on a Chromium bug inquiring about this behavior, Cache-Control: no-store
will override this behavior in Chrome, but this is subject to change.
Besides Cache-Control: no-store
or appending a cache-busting query parameter to the URL, which also disables revalidation via If-None-Match
, I know of one other way of bypassing the ignore higher-level caching flag: load the image data via XMLHttpRequest
, setting xhr.responseType = 'arraybuffer'
, create a Blob
object with the response data, and then load the blob into an Image
with createObjectURL
:
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
var img = new Image();
img.onload = function() {
// ... do something with img
URL.revokeObjectURL(img.src);
};
var blob = new Blob([new Uint8Array(xhr.response)], { type: 'image/png' });
img.src = URL.createObjectURL(blob);
};
xhr.send();