为什么Firefox会忽略缓存头并在刷新时重新验证?

4

我有一些不可变的图像资源,可以永久缓存。Chrome似乎尊重我的响应头,不会重新验证这些资源:

Chrome requests

这是Chrome中这些资源的一个示例。正如您所看到的,我包括cache-control: public, max-ageexpiresetaglast-modified,并且该资源是从“内存缓存”中提供的。

Chrome request


然而,Firefox并不遵守这些头信息,并且在每次加载时重新验证资源!每次页面加载时,我的服务器都会收到有关每个个人资料图片的请求,并返回304:

Firefox requests

这里是一个导致304响应的请求示例:

Firefox request

我不明白为什么Firefox忽略缓存标头并继续向服务器请求304。我尝试了各种与缓存相关的标头,并阅读了有关可缓存内容的标准"cacheable"。我确保在devtools中启用了缓存。我也尝试关闭devtools,但是在服务器日志中仍然看到304。
我发现这只会在页面刷新时发生。一个普通的刷新,而不是Shift或Shift-Command,而只是一个普通的刷新。这不是我期望的行为。

@KevinChristopherHenry感谢您的提问。我本来想在我的问题中提到的:我已经多次检查过了,而且我没有启用那个功能。我一直希望那是问题所在... - Dmitry Minkovsky
我也尝试关闭了开发者工具,但是在服务器日志中仍然看到了304。是的,max-age=0我不理解。我一直通过刷新(command/control+r)来访问页面,但我会尝试使用页面导航。这很有趣。关于这个问题,这里有很多答案。我会搜索一下,谢谢。 - Dmitry Minkovsky
嗨,我可以确认导航模式会影响行为。使用浏览器的“后退/前进”或基于应用程序的路由,Firefox不会发出请求!这是刷新导致了这种行为。但只是一个简单的刷新,不是shift-或shift-command-。这不是我期望的行为。 - Dmitry Minkovsky
@KevinChristopherHenry 如果你想发表答案,我会投票/接受。感谢你的帮助。 - Dmitry Minkovsky
2个回答

5
简单来说:Firefox在浏览器刷新时会重新验证缓存内容。
这曾经是所有浏览器所做的事情。一个合理的假设是如果用户正在主动刷新页面,那么可能是因为出了问题,他们需要从头开始。现在,随着网站显示实时内容的出现,“刷新”的使用可能会有所不同。
Chrome和Firefox似乎采取了不同的方法来处理此问题。Chrome的方法是使其刷新行为更加智能和复杂,而Firefox选择依赖开发人员通过使用Cache-Control: immutable响应头明确指示非陈旧资源永不需要重新验证。(有关此区别的更多信息,请参见此答案。)
如果这种刷新行为是您的应用程序的重要用例(而不仅仅是您用于调试目的的东西),则在Firefox中添加 Cache-Control: immutable 应该可以解决此问题。(请注意,根据MDN的说法,Firefox仅在https内容上尊重immutable,所以这仍然无法在上面的http测试页面上使用。)

感谢提供历史背景。注意,我尝试使用“Cache-Control: immutable”,但在Firefox中刷新时没有效果。也就是说,我尝试将其作为指令与“max-age”和“public”一起添加。我没有尝试它作为独立的指令,因为Chrome不支持它,而且我也不想尝试多个“Cache-Control”头文件。只要这是Firefox的预期行为,我就可以接受它在刷新时重新验证。 - Dmitry Minkovsky
还没有尝试过,但我认为stale-while-revalidate在这里也可能很有用:https://web.dev/stale-while-revalidate/。根据我对该页面的粗略浏览,至少在检索304时会显示“陈旧”的图像。 - Dmitry Minkovsky
@DmitryMinkovsky:如果您关心的是页面的响应能力(而不是服务器的负载,因为请求数量相同),那么这可能是一个好的解决方案。即使浏览器通常支持它,但在刷新时仍然可能不使用它。immutable似乎是更好的解决方案,但我不确定为什么在您的情况下它不起作用。 - Kevin Christopher Henry
2
不知道为什么 immutable 也不起作用。我刚刚尝试将其添加到我的缓存控制指令列表中,但没有成功。无论如何,在刷新时,FF 都会重新验证。stale-while-revalidate 似乎也不能加速事情的进行,并且它还会重新验证。我想这就是 FF 的工作方式。 - Dmitry Minkovsky
1
看起来 MDN 现在解释说:“在 Firefox 中,immutable 只在 https:// 事务上受到尊重。有关更多信息,请参见此博客文章。” 我相当确定我在本地运行并通过 http:// 发布了这个问题:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control - Dmitry Minkovsky
@DmitryMinkovsky:好发现,感谢更新。是的,您的截图显示您使用了 http。我已经编辑了答案。 - Kevin Christopher Henry

1

为了更加详细地说明ETags,让我详细解释一下。 从技术上讲,Firefox正在正确处理它。 当存在Etag时,客户端或中介缓存需要执行GET或HEAD,以确保提供的内容未更改。

If-None-Match标头告诉服务器它有哪个版本或版本。 如果没有更改,服务器/代理将返回304。

您可以通过将弱ETag使其重新验证行为。 方法是在ETags值前面包含W /,即ETag:/ W#########。 这可以在您的服务器上处理,或者如果您无法控制它,则可以通过CDN上的重写规则处理。

对于Firefox的可能解决方法是添加Cache-Control Immutable选项。 Cache-Control: Immutable。 没有查看Firefox缓存启发式代码,我无法确定,但应该很容易测试。

如果不支持它,则其他服务器将忽略该标头。

Etags也无法与字节范围请求一起使用。 因此,如果您没有特定原因使用它们,请考虑关闭它们。


嘿,我会尝试,谢谢。If-None-Match超出了我的控制范围...因为我包含了一个Etag所以它被包括在里面。我不记得我是否尝试过不包括Etag。 我曾经看过immutable,但我不确定我是否可以与public,max-age一起指定它,以及它如何与其他不支持它的浏览器交互。我也留下了这个评论:https://dev59.com/Grroa4cB1Zd3GeqPcAn8?noredirect=1#comment107753570_60902008 - Dmitry Minkovsky
你需要 Etag 吗?如果需要,你也可以尝试将其转换为允许使用缓存副本的弱 Etag。Etag: W/.............. 这里是关于它们的维基文章,https://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation。 - John Scharber
不,ETag 的存在并不能防止浏览器缓存。它只是提供了一种机制,在资源过期时重新验证资源。OP 看到的行为是因为他们正在刷新页面,而 Firefox 中的刷新会触发重新验证。如果重新验证头部不存在,刷新将导致更昂贵的常规请求。 - Kevin Christopher Henry
他仍然有过期和上次修改时间,所以结果仍然将是IMS发送到服务器,就像他正在获取数据一样。我的大部分经验来自于构建三个CDN,我不能确切地说Firefox会怎么做,但我知道我们会如何处理它。 - John Scharber
看起来这个问题在这里讨论:https://dev59.com/AXNA5IYBdhLWcg3wNrEy#30181543。 max-age=0 和重新验证是因为我在刷新。返回、前进和历史API导航不会引起重新验证。有趣的是Chrome不会这样做。我测试了添加“immutable”并删除“etag”,但这没有改变行为。@KevinChristopherHenry - Dmitry Minkovsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接