浏览器的 HTTP 缓存如何工作?

考查重点:缓存新鲜度与条件请求。新鲜副本直接复用;过期后携 ETag 等验证,未变返回304复用内容,变化则取新响应

本题目录

参考答案

核心回答

浏览器的 HTTP 缓存会保存可缓存的响应,后续访问时,先判断是否有匹配的副本,再判断能否直接复用,必要时向服务器验证。

  1. 没有可用副本:正常请求资源,服务器返回内容与缓存策略;浏览器按策略决定是否保存。
  2. 副本仍新鲜且允许直接复用:直接用本地内容,不为这次资源访问发送网络请求,通常称为“强缓存”。常见的新鲜度依据是 Cache-Control: max-age,也可以使用 Expires。
  3. 副本需要验证:若有验证器,发送带 If-None-Match 或 If-Modified-Since 的条件请求,通常称为“协商缓存”。未变化时返回 304,无需重传响应体,浏览器更新相应缓存元数据并复用原内容;变化时通常返回 200 和新内容。没有验证器时,不能凭空协商,通常需要重新获取内容。

两者的区别是:直接复用省掉这次网络请求,协商复用仍有网络往返,主要节省响应体传输。

新鲜度与验证器分别管什么?

max-age=600 表示响应的最大新鲜寿命为 600 秒,要结合响应当前的年龄判断。经过 CDN 等中间缓存时,还要考虑 Age,不能每到一个浏览器都重新获得完整的 600 秒。Expires 给出绝对过期时间;同时存在有效的 max-age 时,按 max-age 确定新鲜寿命。

新鲜度回答“现在可否不问服务器就用”,验证器回答“服务器上的表示是否还是同一版”:

  • ETag / If-None-Match:服务器给响应一个版本标识,客户端在校验时带回。ETag 可以基于内容哈希或版本号生成,并不规定必须怎样计算。
  • Last-Modified / If-Modified-Since:基于最后修改时间校验。它的时间精度和服务端记录方式可能限制判断能力,例如同一秒内多次变化。

两组条件头同时出现时,If-None-Match 优先,服务端忽略 If-Modified-Since,不是先比较 ETag,再把修改时间当第二道条件。

例如某 CSS 响应带 max-age=600 和 ETag: "v1":普通重复访问且仍新鲜时直接复用;需要验证时带 If-None-Match: "v1"。服务端确认仍是该表示就返回 304,否则返回新 CSS 和新的验证器。具体是否发请求,还会受刷新方式和请求的缓存指令影响。

Cache-Control 中几个容易混淆的值

  • no-cache:可以存储,但复用前需要成功验证。它不是“不保存”。
  • no-store:要求缓存不要存储这次请求和响应,也不能用该响应满足后续请求;它不会自动清除此前已经保存的同 URL 副本。
  • private:限制共享缓存存储,浏览器这类私有缓存仍可能保存;它不等于“不缓存”。
  • public:明确允许共享缓存存储,不能随意用于个性化响应。
  • must-revalidate:副本过期后,必须成功验证才能复用,不能因为离线或服务器出错就直接返回过期内容。

没有设置 Cache-Control 也不等于必定不缓存,某些响应可以按规则进行启发式缓存。实际项目应明确配置策略。

项目中怎么设置和排查?

对内容带哈希的静态资源,例如 app.a1b2c3.js,可以设置较长的新鲜期;内容变化就生成新 URL,并更新 HTML 引用。immutable 可以表达该 URL 的内容在新鲜期内不会变化。入口 HTML 则通常采用短新鲜期或 no-cache,让用户能够及时发现新资源。

用户相关响应要考虑私有性与更新要求,例如允许浏览器存储但每次验证,可以组合 private, no-cache;不希望被 HTTP 缓存保存的敏感响应可以用 no-store。账户切换等场景还要处理旧内容,不能只加一个 private 就认为完成了用户隔离。

排查时看开发者工具中的响应缓存头、请求条件头、是否来自 memory/disk cache,以及真实网络响应是否为 304。同时检查 URL 与 Vary:同一 URL 如果按语言返回不同内容,应通过 Vary: Accept-Language 等方式区分副本。发布后只修改源站文件或刷新 CDN,也不会主动让仍新鲜的浏览器副本失效。

这里讨论的是 HTTP 响应缓存;localStorage、应用内缓存、Service Worker Cache Storage 和页面前进后退缓存还有各自机制。协商缓存使用条件请求,不是 CORS 的 OPTIONS 预检。

参考资料

  • 国内:JavaGuide:访问网页的全过程:采用“浏览器缓存检查”部分,把直接复用、发条件请求和真正获取新内容放进一次资源访问流程。
  • 国内:小林coding:HTTP 常见面试题:采用强缓存与协商缓存部分,解释 Expires、Cache-Control、两组验证器和 304 的基本作用。
  • 国内:阿里云:配置 CDN 缓存过期时间:采用生产环境中的版本化文件名和缓存时长取舍,说明为什么静态资源更新要配合新 URL,以及长缓存带来的更新延迟。
  • 国外:MDN:HTTP caching:采用新鲜度、Age、验证器和重新加载部分,解释缓存年龄、ETag 的生成方式,以及 no-cache 与 no-store 的差别。
  • 国外:IETF:RFC 9111 — HTTP Caching:采用缓存复用、Vary、条件验证和缓存指令规定,明确 If-None-Match 的优先级、304 元数据更新与私有/共享缓存边界。

相关面经

5 篇

字节跳动

前端开发工程师
一面
查看原面经 →
浏览器缓存策略

字节跳动

前端开发工程师(Agent)
校招秋招一面
查看原面经 →
展开讲讲浏览器缓存

智元机器人

前端开发工程师
实习一面
查看原面经 →
浏览器缓存机制是什么?强缓存和协商缓存分别是什么?

虾皮

前端开发工程师
日常实习一面
查看原面经 →
http缓存机制、强缓存和协商缓存

阿里巴巴

前端开发工程师
春招二面
查看原面经 →
了解浏览器的缓存机制吗

收录题集

1 个来源,共2 份题集

公司专题代表问法

Onefly · zero2Agent · 1 条原题

查看题集 →

工程化踩坑:死循环、状态丢失与成本控制

Onefly · zero2Agent · 1 条原题

查看题集 →

相关题目

4 题