前面十篇都在讲「怎么把数据传过来」。但 HTTP 还有个反向的问题:同一个资源,能不能别每次都传?
一个网站的 logo、CSS、JS,可能几个月都不变。如果每次打开页面都重新下载,既慢又费带宽。HTTP 缓存就是为解决这个问题设计的。缓存分两道关卡——强缓存和协商缓存,这是缓存阶段的开始。
这一篇讲强缓存,它是 HTTP 缓存里最快的一道:快到连请求都不发。
强缓存:浏览器自己决定用不用,不问服务器
强缓存的核心是:资源在有效期内,浏览器直接用本地副本,根本不发请求给服务器。
从浏览器 DevTools 看,强缓存命中的请求状态是 (disk cache) 或 (memory cache)——请求确实没发出去,资源直接从本地读。这是性能最优的路径:0 网络往返、0 服务器压力。
强缓存由两个响应头控制:
Cache-Control(HTTP/1.1,现代标准):Cache-Control: max-age=3600表示这个资源缓存 3600 秒(1 小时)。Expires(HTTP/1.0,老标准):Expires: Wed, 27 Jun 2026 12:00:00 GMT表示这个资源在这个绝对时间前都有效。
两者同时存在时,Cache-Control 优先级更高(这是为了兼容 HTTP/1.0 客户端才保留 Expires)。现代应用都用 Cache-Control。
Cache-Control 的关键指令
Cache-Control 不只是 max-age,它有一组指令控制缓存行为:
最常用的几个:
max-age=N:缓存 N 秒。这是最核心的指令。no-cache:名字有误导性——它不是「不缓存」,而是「每次用之前必须问服务器」(走协商缓存)。强缓存被禁用了。no-store:真正的不缓存。任何地方都不许存这个响应,每次都要重新请求。用于敏感数据。public/private:public允许中间代理缓存,private只允许浏览器缓存(针对单个用户的内容,比如个人页面)。immutable:表示这个资源永远不会变。配合文件指纹(如app.a3f9.js),浏览器甚至不会在刷新时发条件请求。
这里最容易踩的坑是 no-cache 和 no-store 的区别。no-cache 不是不缓存——它缓存了,但每次用之前要去问服务器「还能用吗」(协商缓存)。no-store 才是真正的彻底不缓存。很多人把 no-cache 当「禁用缓存」用,结果发现资源还是被缓存了,就很困惑。
强缓存的失效:过期了怎么办
强缓存只在「有效期内」生效。一旦 max-age 过期,强缓存就失效了。但这不意味着资源变了——只是浏览器不敢直接用了,要进入下一道关卡:协商缓存(下一篇讲),问服务器「我这副本还能用吗」。
强缓存的「有效期」是基于响应生成那一刻计算的,不是基于「客户端什么时候收到」。服务器在响应里带上 Date 头说明响应生成时间,max-age 从这个时间开始算。这就是为什么缓存的资源在不同设备上失效时间可能略有不同——取决于它们各自什么时候收到的。
怎么给资源定有效期
强缓存的有效期怎么定?关键原则是:按资源的「变不变」来分。
- 带指纹的资源(JS/CSS/图片哈希命名):变一次就换文件名(
app.a3f9.js→app.b7e1.js)。这种资源可以缓存很久(max-age=31536000,一年),因为内容变了文件名也变,不存在「用了旧副本」的问题。配合immutable,浏览器连刷新时的验证请求都省了。 - 不带指纹的资源(HTML、API 响应):内容会变但文件名不变,这种不能缓存太久。HTML 通常用
no-cache(每次验证),API 响应看业务,可能max-age=0或no-store。
这个「按变不变分」的原则是前端性能优化的基础:构建工具(webpack/vite)自动给 JS/CSS 加文件指纹,就是为了能对它们用激进的强缓存。
取舍与边界
强缓存有几个边界值得注意:
- 用户手动刷新(F5)会绕过强缓存。F5 会发请求验证(走协商缓存),Ctrl+F5 会彻底绕过所有缓存重新请求。所以你测缓存策略时别用刷新——它不反映真实用户行为。
- 强缓存在不同浏览器/设备间独立。每个浏览器维护自己的缓存,一台设备上的缓存不影响另一台。
must-revalidate:表示缓存过期后,必须向服务器验证,不能用过期的副本应急。正常情况下缓存过期就该验证,但在某些场景(如断网)浏览器可能用过期副本兜底,must-revalidate禁止这种兜底。
收束:强缓存是最快但最粗的缓存
把三种路径的耗时摆在一起看,强缓存的价值就一目了然:
强缓存是 HTTP 缓存的第一道关卡,快到零网络开销,但它的判断很粗——只看「过期没」,不关心「内容到底变没变」。过期了就失效,哪怕内容其实没变。
下一篇讲协商缓存,它是强缓存失效后的第二道关卡:不发完整资源,只问服务器「我的副本还能用吗」,能用就返回 304,省掉传输 body 的开销。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 HTTP」,欢迎关注公众号 「十三Tech」。后续会按 URL 与报文、连接与传输、缓存与协商、安全与边界、HTTP/2 与 HTTP/3 这条线更新。

