Why don't browsers respect Cache-Control heade

2019-08-10 00:41发布

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?

1条回答
狗以群分
2楼-- · 2019-08-10 01:29

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();
查看更多
登录 后发表回答