为什么 ETag 不足以使浏览器缓存失效?

Why isn't ETag alone enough to invalidate the browser cache?

我已经阅读了很多关于此事的相关文章以及关于 HTTP 缓存的非常好的文章: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en#invalidating-and-updating-cached-responses 但我还是不清楚:

为什么发送 ETag header 不足以使特定资源的浏览器缓存失效?为什么每个人都建议实际更改资源的 URL/filename 以强制浏览器 re-download 文件?如果浏览器已经缓存了带有特定 ETag 的文件,并且在服务器上修改了 ETag,这还不够吗?

我发现以下页面很有帮助:

来自 MDN 的 ETag 页面的这一行共享关键点(强调已添加):

If a user visits a given URL again (that has an ETag set), and it is stale, that is too old to be considered usable, the client will send the value of its ETag along in an If-None-Match header field...

一旦资源变为 "stale",客户端将使用 ETag 重新验证资源。但是什么构成了"stale"?

这就是 Cache-Control header 派上用场的地方。 Cache-Control header 可以与响应一起发送,让客户端知道客户端可以缓存一个项目多长时间,直到它应该被认为是陈旧的。例如,Cache-Control: no-cache 表示应立即将资源视为过时。有关可用 Cache-Control 值的更多信息,请参阅 MDN Cache-Control page

当浏览器尝试处理对被认为过时的缓存资源的请求时,它会首先向服务器发送重新验证请求,并通过 If-None-Match 请求 [=85] 包含资源的最后一个 ETag 值=],如 MDN's ETag page 所述。它也可以使用作为 If-Modified-Since 请求 header 发送的 Last-Modified 响应 header 作为次要选项。

如果服务器确定客户端的 ETag 值(在 If-None-Match 请求 header 中)是最新的,那么它将以 304(未修改)HTTP 状态代码响应和一个空的 body,表示客户端可以使用缓存的条目。否则,服务器将响应 200 HTTP 状态代码和新响应 body.

其他资源:

直接回答您的问题:

  • 为什么发送 ETag header 不足以使特定资源的浏览器缓存无效? -- 因为 ETag header 不是验证直到缓存的条目被认为是陈旧的,例如通过 Cache-Control 响应中设置的到期日期 header.
  • 为什么每个人都建议实际更改资源的URL/filename以强制浏览器re-download文件? -- 更改URL/filename 或添加查询字符串将强制客户端避免使用缓存。这很简单,也是一种无形中保证cache-busting的方式。这并不意味着它是必要的,但在浏览器行为不一致的情况下它往往是安全的。
  • 如果浏览器已经缓存了带有特定 ETag 的文件,并且 ETag 在服务器上被修改,这还不够吗? -- 技术上应该足够了因为包含了适当的 Cache-Control headers(包括 PragmaExpires headers)。有关详细信息,请参阅 How to control web page caching, across all browsers?