为什么HTTP响应中应该同时使用no-cache和no-store?

156

我被告知为了防止用户信息泄露,仅在响应中使用“no-cache”是不够的。还需要使用“no-store”。

Cache-Control: no-cache, no-store

阅读了这份规范http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html后,我仍然不太确定原因。

我的理解是它只是用于中间缓存服务器。即使响应中含有“no-cache”,中间缓存服务器仍然可以将内容保存到非易失性存储器中。中间缓存服务器将决定是否在后续请求中使用保存的内容。但是,如果响应中含有“no-store”,则中间缓存服务器不应该存储内容。所以,更加安全。

除了以上原因,我们需要同时使用“no-cache”和“no-store”吗?


6
“no-cache”并不是你想的那样。实际上,它的意思是“请重新验证”。 - Erwan Legrand
@ErwanLegrand 那真是个很棒的名字 - undefined
13个回答

109

我必须澄清,no-cache并不意味着不要缓存。实际上,它的意思是在每个请求之前,使用任何可能存在的缓存响应之前,与服务器重新验证。

另一方面,must-revalidate只需在资源被视为陈旧时重新验证。

如果服务器表示该资源仍然有效,则缓存可以响应其表示,从而减轻了服务器重新发送整个资源的需要。

no-store实际上是完全的不要缓存指令,并且旨在防止以任何形式将表示存储在缓存中。

我说“任何形式”,但是请注意RFC 2616 HTTP规范中的以下内容:

历史记录缓冲区可以将这些响应作为其正常操作的一部分存储

但是,在新的RFC 7234 HTTP规范中省略了此内容,可能是为了使no-store更加强大,详见:

https://www.rfc-editor.org/rfc/rfc7234#section-5.2.1.5


39
为什么在HTTP响应中需要同时使用"no-cache"和"no-store",而不是只用"Cache-Control: no-store"?为什么? - Franklin Yu
浏览器之间有差异吗?因为微软的这篇文章 https://learn.microsoft.com/en-us/iis/configuration/system.webServer/staticContent/clientCache 甚至没有提到 no-store,并且将 no-cache 描述为根本不缓存...我感到困惑! - Roel
2
Alconja的答案是针对这个问题的答案。当我回答时,只是为了澄清一个非常普遍的误解。请投赞成另一个答案! - Luke Puplett
微软文档的解释是错误的,但实现似乎是正确的。IIS将发送“no-cache”和“ETag”。如果不使用缓存,“ETag”就没有用了。IIS确实会发送“304”。 - ShortFuse
1
@FranklinYu 您所需要的答案是:IE 10 版本之前仅使用 no-cache 作为 Cache-Control 标头,因此我们需要在旧版 IE 和其他浏览器上同时使用 no-cacheno-store 才能使其正常工作。请参见:https://learn.microsoft.com/zh-cn/troubleshoot/developer/browsers/connectivity-navigation/how-to-prevent-caching - didxga

57

在某些情况下,即使响应头中包含Cache-Control: no-cache,IE6仍然会缓存文件。

W3C关于no-cache的规定:

如果no-cache指令没有指定字段名,则缓存必须在与源服务器成功重新验证之前,不得使用响应来满足后续请求。

在我的应用程序中,如果您访问了带有no-cache头的页面,然后注销,然后在浏览器中点击“返回”按钮,IE6仍会从缓存中获取页面(而没有向服务器发出新的/验证请求)。加入no-store标头可以阻止这种行为。但是,如果你相信W3C的话,实际上没有办法控制这种行为:

历史缓冲区可能会将这些响应作为其正常操作的一部分而存储。

浏览器历史记录和正常的HTTP缓存之间的一般区别在规范的特定子段中描述


9
当您在浏览器中点击“返回”时,IE6不会从缓存中获取页面,而是从历史记录缓冲区获取页面。 - Pacerier
2
在Chrome 34(2014年)中,仍然需要设置“no-store”。否则,在使用后退按钮时,Chrome将显示缓存/缓冲数据。 - caw
6
-1是因为第一句话错误地暗示浏览器缓存了一个“no-cache”头的响应是不正确的。下面的W3C引用清楚地说明这不是事实;相反,“no-cache”头只是意味着在重复使用响应以服务后续请求之前,必须重新验证响应。 - Mark Amery
1
规范的措辞已经从RFC1616改进到了当前版本的规范(https://tools.ietf.org/html/rfc7230系列的RFC)。这是一个系列,因为它包含了6个RFC。它们废除了2616。 - Arcin B

17

no-store在正常情况下并不是必需的,有时甚至可能影响速度和可用性。

它旨在作为隐私保护措施:它告诉浏览器和缓存,响应包含敏感信息,不应该被写入磁盘缓存(或其他非易失性存储)。

工作原理:

  • 通常情况下,即使服务器将响应标记为 no-cache ,用户代理(如浏览器)仍然可以缓存响应。如果用户代理需要稍后再次请求相同的资源,则可以进行条件请求,服务器可以决定发送更新的响应还是允许用户代理重用存储的响应。

    如果服务器未明确确认存储的响应仍然可以使用,或者无法访问服务器,则用户代理(例如浏览器)无法使用存储的响应。

  • 使用 no-store 将阻止将响应存储到磁盘或任何非易失性存储中,并防止响应在以后的条件请求中使用。

我的当前理解是它只是用于中间缓存服务器。即使响应中有“no-cache”,中间缓存服务器仍然可以将内容保存到非易失性存储器中。
这是不正确的。与HTTP 1.1兼容的中间缓存服务器将遵守与浏览器缓存相同的“no-cache”和“must-revalidate”指令。
如果中间缓存服务器不支持HTTP 1.1,则您需要使用“Pragma: no-cache”,并希望一切顺利。请注意,如果它不支持HTTP 1.1,则“no-store”也不会产生所需的效果。

5
因为http://www.mnot.net/cache_docs/#CACHE-CONTROL与您的观点相矛盾,所以我是否有误解?该网页指出,“no-cache”可保持严格的新鲜度,同时不牺牲所有缓存的好处,这意味着如果服务器响应304未修改状态,缓存将被存储并再次使用。 - Pacerier
2
-1:no-cache并不意味着内容不能被缓存。在14.9.1可缓存性规范中指出:“如果no-cache指令没有指定字段名,则缓存必须在与源服务器成功重新验证之前,不得使用响应来满足后续请求。”(http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.)正如Chris Shiflett所解释的那样,“它并不会阻止缓存系统保留缓存副本。它只是要求缓存系统在将其发送回客户端之前重新验证其缓存。”(HTTP开发人员手册,第91页) - james.garriss
1
我认为我在这个答案中写的内容并不违反那两条评论中的任何一条 - 我只是没有谈论浏览器如何重新验证(例如使用If-Modified-Since / If-None-Match),因为我认为这与问题无关。我甚至没有尝试解释no-cache的作用,所以我很难理解@james.garriss的评论与我的回答有什么关系。 - thomasrutter
刚刚解释了我对何时需要使用“no-store”的疑惑。 - John Xiao
重新阅读了我十年前的回答后,发现其中有不少是错误的。我已经进行了编辑以删除不正确的信息。 - thomasrutter

14

来自HTTP 1.1规范:

no-store:

no-store指令的目的是防止意外发布或保留敏感信息(例如备份磁带上的信息)。 no-store指令适用于整个消息,并且可以在响应或请求中发送。如果在请求中发送,则缓存不得存储此请求或任何响应的任何部分。如果在响应中发送,则缓存不得存储此响应或引发它的请求的任何部分。此指令适用于非共享和共享缓存。在此上下文中,“MUST NOT store”意味着缓存不得有意将信息存储在非易失性存储器中,并且必须尽最大努力在转发信息后尽快从易失性存储器中删除信息。 即使将此指令与响应相关联,用户也可能明确地将此类响应存储在缓存系统之外(例如,使用“另存为”对话框)。历史缓冲区可以将这些响应作为其正常操作的一部分存储。 此指令的目的是满足某些用户和服务作者的声明要求,他们担心通过未经预期的访问缓存数据结构而意外释放信息。虽然在某些情况下使用此指令可能会提高隐私性,但我们警告它根本不是确保隐私的可靠或充分机制。特别是,恶意或受损缓存可能无法识别或遵守此指令,并且通信网络可能容易被窃听。


1
如果您尚未缓存请求,那么这不会已经防止响应存储在非易失性介质中吗? - Lèse majesté
4
通常情况下不会。no-cachemax-age=0 表示该项被视为陈旧的,因此在提供之前必须重新验证。这意味着缓存可以存储文件,然后执行条件请求,服务器可以回复 304 NOT MODIFIED。显然,这是一个巨大的优势,因为无需生成和发送响应体。因此,为了利用这个优势,许多(大多数?)缓存将存储 no-cache 响应。 - Kevin Cox

11

对于Chrome来说,no-cache用于在重新访问页面时重新加载页面,但如果你通过历史记录(后退按钮)返回页面,它仍然会缓存该页面。要同时为历史后退重新加载页面,请使用no-store。IE需要使用must-revalidate才能在所有情况下正常工作。

因此,为了避免所有的错误和误解,我总是使用

Cache-Control: no-store, no-cache, must-revalidate

如果我想确保它重新加载。


11

8
为什么对于Internet Explorer而言,no-store不足以满足要求?您的博客文章中没有解释。 - Simon Lieschke
1
你在谈论哪个IE版本? - Pacerier
1
@Pacerier,可能当时他/她写评论的时候使用的是最新的IE版本。根据维基百科,这应该是IE7。对于FF来说,看起来是3。现在几乎没有多少人再使用它们了。 - trysis

9

如果缓存系统正确实现了no-store,则不需要使用no-cache。但并非所有缓存系统都如此。另外,一些浏览器像no-store一样实现了no-cache。因此,虽然不是严格要求,但最好同时包含两者。


1
但并非全部都是这样的。” 我们需要一个具体的例子来说服我的同事。 - Franklin Yu
1
那个评论是6年前发表的。你需要调查缓存服务器的当前行为,看看它们在做什么。 - james.garriss

6
请注意,Internet Explorer 5至8版本在尝试通过https下载文件时,如果服务器发送Cache-Control: no-cachePragma: no-cache头信息,则会抛出错误。
请参见http://support.microsoft.com/kb/812935/en-us 使用Cache-Control: no-storePragma: private似乎是仍然可用的最接近的方法。

3
相关的SO答案所建议,您可以按照确切的顺序设置Cache-Control: no-store, no-cache, must-revalidate来实现此目的。然而,在我们的场景中这并没有生效,但是@bassim上面提到的方法确实有效。谢谢! - Eirik H

3

最初我们多年前使用了no-cache,并且在某些浏览器中遇到了过期内容的问题...不幸的是,我不记得具体情况了。

此后,我们只使用了no-store。自那以后,任何浏览器或中介机构都没有出现过过期内容的问题。

这个领域确实被现实的实现所主导,而不是各种RFC中写的内容。许多代理尤其倾向于认为他们可以通过替换他们应该遵循的策略来“提高性能”。


我相信以前喜欢使用“no-store”的是Firefox。 - bvdb

3
回答这个问题,有两个参与者,客户端(请求)和服务器(响应)。
客户端:
客户端只能使用一种缓存方法进行请求。有不同的方法,如果未指定,则会使用默认值。
  • default: 检查浏览器缓存:
    1. 如果已经缓存并且“新鲜”:从缓存中返回。
    2. 如果已经缓存,但是“陈旧”,但仍然“有效”:从缓存中返回,并安排一个获取以更新缓存(供下一次使用)。
    3. 如果已经缓存并且“陈旧”:带条件获取、缓存和返回。
    4. 如果没有缓存:获取、缓存并返回。
  • no-store: 获取并返回。
  • reload: 获取、缓存并返回。(默认-4
  • no-cache: 检查浏览器缓存:
    1. 如果已经缓存:带条件获取、缓存并返回。(默认-3
    2. 如果没有缓存:获取、缓存并返回。(默认-4
  • force-cache: 检查浏览器缓存:
    1. 如果已经缓存:无论是否过期都返回它。
    2. 如果没有缓存:获取、缓存并返回。(默认-4
  • only-if-cached: 检查浏览器缓存:
    1. 如果已经缓存:无论是否过期都返回它。
    2. 如果没有缓存:抛出网络错误。
注释:
  • 仍然“有效”意味着当前的agestale-while-revalidate的生命周期内。它需要“重新验证”,但仍可返回。
  • 这里的“获取”(Fetch)为简单起见,是指“非条件网络获取”。
  • “带条件获取”(Fetch with conditions)意味着使用诸如If-Modified-SinceETag等头来获取,以便服务器可以响应304:(未修改)

https://fetch.spec.whatwg.org/#concept-request-cache-mode

服务器:
现在我们了解了客户端的功能,服务器响应就更有意义了。查看Cache-Control头部,如果服务器返回:
- no-store:告诉客户端根本不要使用缓存 - no-cache:告诉客户端应该进行条件请求并忽略新鲜度 - max-age:告诉客户端缓存保持“新鲜”的时间 - stale-while-revalidate:告诉客户端缓存保持“有效”的时间 - immutable:永久缓存
现在我们可以把它们结合起来。这意味着唯一的可能性是:
  • 非条件网络获取
  • 有条件的网络获取
  • 返回陈旧的缓存
  • 返回陈旧但有效的缓存
  • 返回新鲜的缓存
  • 返回任何缓存
任何客户端或服务器的组合都可以决定使用哪种方法或一组方法。如果服务器返回no-store,无论客户端请求类型如何,它都不会命中缓存。如果客户端请求是no-store,不管服务器返回什么,它都不会缓存。如果客户端没有指定请求类型,则服务器将用Cache-Control来指定。
服务器同时返回no-cacheno-store是没有意义的,因为no-store会覆盖所有设置。是的,你可能经常看到这两个选项一起使用,但在浏览器实现正常的情况下,这是无用的。不过,no-store自1999年以来就已经成为规范的一部分了:https://datatracker.ietf.org/doc/html/rfc2616#section-14.9.2 在实际使用中,如果你的服务器支持304:未修改,并且想使用客户端缓存来提高速度,但仍然希望强制进行网络获取,请使用no-cache。如果不支持304,并且想强制进行网络获取,请使用no-store。如果你可以接受有时候使用缓存,请使用新鲜度和重新验证头信息。
实际上,如果在客户端混淆了no-cacheno-store,那么几乎不会有什么变化。然后,只会发送几个头文件,并且浏览器将处理不同的内部响应。如果使用no-cache,然后忘记稍后使用它,可能会出现问题。no-cache告诉浏览器将响应存储在缓存中,而没有它的后续请求可能会触发内部缓存。
有时,您可能希望根据上下文甚至在同一资源上混合使用方法。例如,您可能希望在服务工作者和后台同步上使用reload,但在网页本身上使用default。这就是您可以按照自己的意愿操纵用户代理(浏览器)缓存的地方。只需记住,服务器通常最终决定缓存的工作方式。
为了澄清可能发生的混淆,客户端 可以在 请求 中使用 Cache-Control 头部,告诉服务器在响应时不要使用自己的缓存系统。这与浏览器/服务器动态无关,更多地涉及服务器/数据库动态。
另外,no-store 在技术上意味着不能存储到任何非易失性存储(磁盘)并尽快从易失性存储(内存)中释放。实际上,这意味着根本不使用缓存。这个命令实际上是双向的。带有 no-store 的客户端请求不应写入磁盘或数据库,并且是短暂的。
TL;DR: no-store 会覆盖 no-cache。同时设置这两个选项是无用的,除非我们在讨论超出规范或不支持 no-store 的 HTTP/1.0 浏览器(例如 IE11)。使用 no-cache 支持 304

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