跳到正文
效率工具实用笔记关于本站
浏览器工具

Cache-Control的private和public怎么选:个性化内容与共享缓存的边界

约 8 分钟阅读AI 辅助整理

private让响应只进私有缓存,public让响应可进共享缓存。个性化响应必须加private,否则可能被共享缓存或请求合并泄露给其他用户;public只在需要让带Authorization头的响应可存储时才用。

本文要点

  1. 如果你返回的响应内容因人而异,比如登录后显示的昵称、订单列表、个人设置,那么这份响应必须加private

  2. public不是private的反义词,也不是“允许缓存”的通用开关

  3. 这是private最容易被忽略的一个作用点

Cache-Control的private和public怎么选:个性化内容与共享缓存的边界的知识要点示意图
依据已审核正文摘取要点制作的示意图,非实物照片;请结合正文中的条件与范围阅读。要点摘自已审核正文;完整条件与说明见正文。查看高清图解 ↗
本文目录

先分清两种缓存:私有缓存和共享缓存各存谁的响应

如果你在响应头里看到private或public,先要弄清它们各自在跟哪种缓存说话。

按MDN的HTTP缓存说明,缓存分两类:私有缓存是绑定到特定客户端的缓存,通常就是浏览器缓存,它存储的响应不与其他客户端共享;共享缓存位于客户端和服务器之间,可以存储能在用户之间共享的响应,进一步分为代理缓存和托管缓存。

这个区分决定了private和public的作用范围。private是在告诉共享缓存“这份响应不要存”,public是在告诉缓存“这份响应可以存,即使有Authorization头”。

一个自建情境(示例,不是对某个真实网站的记录):你在练习目录里维护一个自建页面,用本地或公开地址打开它,在开发者工具的Network面板里点开响应头,看Cache-Control这一行写的是private还是public。下面所有判断都围绕这一行展开。

个性化响应为什么必须加private:cookie不等于私有

如果你返回的响应内容因人而异,比如登录后显示的昵称、订单列表、个人设置,那么这份响应必须加private。

MDN的说明很直接:如果响应包含个性化内容并且你只想将响应存储在私有缓存中,则必须指定private指令。原因是,如果个性化内容存储在私有缓存以外的缓存中,其他用户可能能够检索到这些内容,这可能导致无意的信息泄露。

这里有一个容易踩的坑:个性化内容通常由cookie控制,但cookie的存在并不能表明它是私有的,因此单独的cookie不会使响应成为私有的。也就是说,你不能因为响应里带了Set-Cookie,就默认缓存会把它当成私有内容处理。

所以判断顺序是:先问这份响应是不是因人而异,如果是,就加private;不要用“有没有cookie”来代替这个判断。

public什么时候用:只在带Authorization头的响应需要被存储时

public不是private的反义词,也不是“允许缓存”的通用开关。

按MDN的说明,public值具有使响应可存储的效果,即使存在Authorization标头。备注里写得很清楚:只有在设置了Authorization标头时需要存储响应时才应使用public指令。否则不需要,因为只要给出了max-age,响应就会存储在共享缓存中。

这句话可以拆成两层。第一层:没有Authorization头时,你不需要public,响应照样能进共享缓存。第二层:有Authorization头时,public会让这份响应变得可存储,而如果这份响应是用基本身份验证做个性化的,public的存在可能会导致问题。

所以public的使用场景很窄:你确实希望一份带Authorization头的响应被共享缓存存储。如果你对此不确定,MDN给的建议是选择不带public的写法。

一个自建示例(练习数据,不是真实配置):

# 带Authorization头,且确实希望共享缓存存储
Cache-Control: public, max-age=31536000

# 不太确定时,不用public
Cache-Control: max-age=2592000

注意这两行只是说明public的有无,不涉及max-age怎么算。

请求合并:即使写了no-cache,个性化响应仍可能被共享

这是private最容易被忽略的一个作用点。

MDN在“请求合并”一节说明:共享缓存主要位于源服务器之前,旨在减少到源服务器的流量。如果多个相同的请求同时到达共享缓存,中间缓存将代表自己将单个请求转发到源,然后源可以将结果重用于所有客户端。

关键句在这里:当请求同时到达时会发生请求合并,因此即使响应中给出了max-age=0或no-cache,它也会被重用。

也就是说,no-cache并不能阻止请求合并把一份响应发给多个客户端。如果响应是针对特定用户个性化的,并且你不希望它在合并中共享,则应添加private指令。

所以对个性化响应来说,private不是可选项,而是必须项。它和no-cache回答的是两个不同问题:no-cache管“用之前要不要验证”,private管“能不能进共享缓存”。

一个组合写法:no-cache加private各管一件事

如果你既要内容始终最新,又要防止个性化内容泄露给其他用户,MDN给出的组合是:

Cache-Control: no-cache, private

这两个指令分工不同。no-cache强制客户端在重用任何存储的响应之前发送验证请求;private则防止响应被共享缓存存储。

MDN在“默认设置”一节也提到:如果服务实现了cookie或其他登录方式,并且内容是为每个用户个性化的,那么也必须提供private,以防止与其他用户共享。

需要划清边界:本文只讲private和public在私有缓存与共享缓存中的区别,以及个性化响应为什么要加private。不讲max-age怎么计算,不讲ETag验证细节,也不讲具体服务器怎么配置。上面出现的max-age数值只是资料原文中的示例,不是本文要教的计算方法。

检查结果:在响应头里确认private有没有生效

写完响应头之后,可以按下面几步做一次核对(以下为编辑整理的检查方向,不是对某个真实网站的测试记录):

1. 打开开发者工具的Network面板,刷新页面,点开目标请求。

2. 看Response Headers里的Cache-Control这一行,确认private是否出现在里面。

3. 如果这份响应是个性化的,确认它没有只写no-cache而漏掉private。

4. 如果这份响应带了Authorization头,确认public是不是你确实需要的,而不是顺手加上的。

一个自建情境(示例):你在练习目录里放一个自建页面,服务器返回的响应头分别写成下面两种,观察Network面板里Cache-Control这一行的差别。

Cache-Control: no-cache
Cache-Control: no-cache, private

这一步只用来核对响应头里有没有private,不据此推断任何真实网站的行为。

适用限制:private和public不能解决什么

private和public只回答“这份响应能不能进共享缓存”这一个问题。它们不能解决以下事情:

- 不能替代身份验证和访问控制。private防止的是缓存层面的泄露,不是接口层面的越权。

- 不能保证内容最新。要保证每次拿到最新内容,需要配合no-cache这类指令,本文不展开。

- 不能控制托管缓存的全部行为。MDN指出,托管缓存的特性因部署的产品而异,某些托管缓存可能故意忽略no-store指令,这种情况下不应视为不符合标准,而应仔细阅读所用托管缓存机制的文档。

- 不能删除已经存储的旧响应。MDN说明,no-store指令阻止存储响应,但不会删除相同URL的任何已存储响应。

本文只讲private和public这两个指令在私有缓存与共享缓存中的区别,以及个性化响应为何要加private。不讲max-age计算、不讲ETag验证细节、不讲具体服务器配置。

观看知识要点视频 18 秒

18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。

参考资料

以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。

请结合原始资料的适用条件阅读。

返回浏览器工具 →