“Cache-Control: max-age=0, no-cache” 但浏览器绕过服务器查询(并使用缓存)?

22
我正在使用Chrome 40(所以是一些漂亮和现代的东西)。
所有页面都设置了“Cache-Control:max-age = 0,no-cache”,因此我期望浏览器仅在首先与服务器进行检查并获得“304未修改”响应后,才从其缓存中使用某些内容。
但是,在按下返回按钮时,浏览器会愉快地访问自己的缓存,而不与服务器进行检查。
如果我在新标签中打开与返回按钮到达的相同页面,则确实会与服务器进行检查(并获取“303 See Other”响应,因为事情已经改变)。
请参见下面的屏幕截图,显示来自Chrome开发人员工具的网络选项卡的两种不同情况的输出。
我认为我可以使用“max-age = 0,no-cache”作为“no-store”的轻量级替代方案,其中我不希望用户通过返回按钮看到陈旧的数据(但数据是非有价值的,因此可以缓存)。

我对no-cache的理解(请参见SO上的herehere)是,浏览器必须始终重新验证所有响应。那么为什么使用后退按钮时Chrome不这样做呢?

no-store是唯一的选择吗?


按下后退按钮时,200 响应(来自缓存):

enter image description here

在新标签页请求相同页面时,返回 303 响应:

enter image description here

3个回答

33

来自http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1

no-cache

如果no-cache指令未指定field-name,则缓存必须在与源服务器成功重新验证之前,不得使用响应来满足后续请求。这使得源服务器可以防止即使已配置为向客户端请求返回过时响应的缓存也无法缓存。

如果no-cache指令指定了一个或多个field-names,则缓存可以使用响应来满足后续请求,但受到缓存的任何其他限制。然而,指定的field-name(s)必须在未经源服务器成功重新验证的情况下,不得在响应中发送给后续请求。这允许源服务器防止在响应中重用某些头字段,同时仍允许缓存响应的其余部分。

除名称外,no-cache 不要求响应不能被缓存。它只指定缓存的响应必须在不重新验证的情况下不被重复使用以服务于后续请求,因此它是 must-revalidate, max-age=0 的速记形式。

浏览器将什么视为后续请求由浏览器决定,据我所知,使用后退按钮并不算。此行为因不同的浏览器引擎而异。

no-store 禁止对所有请求使用缓存的响应,而不仅仅是后续请求。

请注意,即使有 no-store,RFC 实际上允许客户端将响应存储在历史记录缓冲区中以供使用。这意味着即使已指定 no-store,客户端仍可以使用缓存的响应。

后面的行为涵盖了页面在浏览器历史记录中记录其原始页面标题的情况。另一个用例是各种移动浏览器的行为,这些浏览器在以下页面完全加载之前不会丢弃前一页,因为用户可能想要中止加载。

关于后退按钮的行为,需要澄清的是:根据http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.13,它不受任何缓存头的限制。

用户代理通常具有历史记录机制,例如“返回”按钮和历史记录列表,可以用于重新显示会话中先前检索到的实体。

历史记录机制和缓存是不同的。特别是,历史记录机制不应尝试显示资源当前状态的语义透明视图。相反,历史记录机制旨在显示用户在检索资源时所见到的内容。

默认情况下,历史记录机制不适用到期时间。如果实体仍然存储在其中,则历史记录机制应该显示它,即使实体已过期,除非用户已经特别配置了代理以刷新过期的历史文档。

这意味着当使用后退按钮时无视任何缓存控制标头是推荐的行为。如果您的浏览器恰好遵守了过期日期并应用no-store指令不仅适用于浏览器缓存还适用于历史记录,那么它实际上已经偏离了该推荐行为。

如何解决:
您不能这样做,也不应该这样做。如果用户返回到先前访问的页面,大多数浏览器甚至会尝试还原视口。如果这是用户离开页面之前的原始行为,则可以使用延迟机制(如AJAX)刷新内容,但否则您甚至不应修改内容。


这听起来是个合理的解释,但它基于什么来源呢?你说“由浏览器决定将什么视为后续请求” - 但这在任何正式文献中有明确说明吗?如果没有参考依据,那这只是对Chrome行为的一种事后辩解。 - George Hawkins
3
历史机制和缓存是不同的。特别是,历史机制不应该尝试显示资源当前状态的语义透明视图。相反,历史机制旨在显示用户在检索资源时看到的确切内容。@GeorgeHawkins http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.13 - Ext3h
这是否意味着每个修改资源的 AJAX 请求,例如 PUTPATCH,都必须始终伴随着重新获取资源的 GET 请求?以便历史机制可以在用户退出时恢复视图?这有点违背了缓存的目的,缓存的目的是减少请求。 - kakarukeys

2

你尝试过使用完整的no-cache头部吗?

<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />

这似乎总是有效的,除非你正在运行一个“pushState”网站。

2

看起来这是Chrome中使用后退按钮的已知“怪异”现象。在此处的错误报告中有关于此问题的良好讨论:

https://code.google.com/p/chromium/issues/detail?id=28035

不幸的是,大多数人似乎都回到了使用no-store。

我认为大多数用户已经习惯了使用后退按钮时不会出现完整页面刷新的体验。如果你考虑大多数管理后退操作的Angular或Backbone应用程序,它们只会刷新内容而不是整个页面。基于这一点,我怀疑当客户回来时刷新或获取更新可能并不出乎意料。


我之前没有考虑到对Angular等的影响,这实际上是我见过的对当前行为最好的解释。谢谢 :) 如果我们能找到来自Google的参考资料,说明当前行为是基于类似这样的有意决定,那就太好了。Google似乎并不会在他们对你所引用的错误的回应中特别澄清任何事情(事实上,我认为他们误解了它——他们将其与我觉得毫不相关的问题合并了)。 - George Hawkins
1
我希望我能够分配赏金,但显然这是不允许的。你提供的错误链接和关于Angular的评论很有启发性,但Ext3h的答案更接近指出为什么行为可以像这样“允许”。 - George Hawkins

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