为什么浏览器不尊重使用 `(new Image()).src = ...` 加载的图像 Cache-Control headers?

Why don't browsers respect Cache-Control headers for images loaded with `(new Image()).src = ...`?

测试用例:

<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>

Chrome 和 Firefox 都不会对第一个 no-cache.png 发出任何网络请求,尽管它是用 Cache-Control: no-cache 提供的,表明用户代理 必须 重新验证缓存的内容。

这里有几个问题 (e.g. this one) 涉及到这个问题并提供了解决方法,但我最感兴趣的是回答更基本的问题:

图像的特殊行为在 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.

正是这个标志控制用户代理是否从可用图像列表中删除条目或不给定 higher-layer 资源的缓存语义(例如 HTTP Cache-Control 响应 header).

标志是设置并且当加载图像源的网络任务完成时图像添加到可用图像列表。

根据询问此行为的 Chromium 错误 this commentCache-Control: no-store 将覆盖 Chrome 中的此行为,但这可能会发生变化。

除了 Cache-Control: no-store 或将 cache-busting 查询参数附加到 URL 之外,这也禁用了通过 If-None-Match 重新验证的方法,我知道另一种绕过忽略的方法higher-level 缓存标志:通过 XMLHttpRequest 加载图像数据,设置 xhr.responseType = 'arraybuffer',用响应数据创建 Blob object,然后将 blob 加载到一个ImagecreateObjectURL:

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();