1. 从一次线上事故说起:缓存不是随便加几个响应头那么简单
记得去年年中,我负责的一个电商后台项目上线了一个紧急修复,改的是某个公共模块的JavaScript逻辑。测试环境一切正常,QA验证通过,然后很自信地点了发布。结果上线半小时内,客服那边炸了,反馈用户在浏览器上看到的白屏和错乱的页面样式。第一反应是回滚,但回滚之后用户那边还是有问题。后来排查发现,问题根本不在服务端代码,而是静态资源的缓存策略太激进,导致线上用户继续用着旧版本脚本,和新版页面接口对不上。
那次事故之后,我花了整整一周把整个项目的前端缓存策略全面梳理了一遍,也把HTTP缓存、浏览器缓存、CDN缓存、Service Worker这些链路彻底吃透了。今天这篇博文,我就把这一整套实战经验完整地分享出来,包含各种配置模板、踩坑记录和排查思路,希望帮你少走弯路。
先说一个重要结论:前端缓存绝对不是"给静态资源加个Cache-Control头"这么简单。真正的缓存策略是一个体系,涉及浏览器、服务器、CDN三层协同,每一层都有各自的职责和坑。如果只是一知半解地乱配,轻则资源更新不及时,重则线上事故。这些内容也是很多前端面试题里高频考察的点,搞清楚底层原理,面试和实战都能稳很多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 缓存体系全景图:先分清谁在缓存你的页面
很多人一提到前端缓存,第一反应就是浏览器缓存。但实际上,当一个用户访问你的网站时,静态资源会经过至少三层的"储存与转发":浏览器缓存、CDN节点缓存、源站服务器。这三层各自独立,又互相配合,每一层失效都会导致下一层被请求,所以性能瓶颈往往出在"你只配好了其中一层"。
2.1 浏览器缓存:离用户最近的第一道防线
浏览器缓存存储在网络请求的响应层,说白了就是浏览器替你把之前请求过的资源(HTML、CSS、JS、图片、字体等)存在本地磁盘或内存中。当再次发起相同请求时,浏览器会根据缓存策略决定"直接使用本地副本""发起条件请求验证"还是"重新下载"。
浏览器缓存最大的价值在于:它让"重复访问"完全绕开了网络传输。对同一个用户来说,第二次打开你网站的加载速度,理论上可以快到接近0毫秒(从磁盘读)。但它的局限也很明显——它只对"同一个浏览器"生效,换个设备、换个人,缓存就完全没用了。也就是说,浏览器缓存解决的是"老用户回访"的体验问题。
2.2 CDN缓存:用户和源站之间的中转站
CDN缓存解决的是"不同用户、不同地区"的加速问题。源站服务器通常部署在某个机房,用户如果在全国乃至全球各地,直接访问源站延迟会很高。CDN会把你网站的静态资源分发到各个边缘节点,用户请求时,CDN会从最近的节点直接返回资源,源站的负载也大大降低。
但这里面有一个常见误区:很多人以为CDN只是"加速",其实它本质上是"分布式缓存"。CDN节点同样遵循HTTP缓存协议,它需要根据你设置的Cache-Control、Expires等响应头来决定缓存多长时间。如果你在源站配置了错误的缓存头,CDN也会跟着错,甚至比浏览器缓存更坑——因为CDN缓存的资源是"所有用户共享"的,一旦某节点缓存了错误版本,影响的就是一批用户。
2.3 源站服务器:缓存策略的"立法者"
服务器是缓存策略的源头,它通过HTTP响应头告诉浏览器和CDN"这个资源可以缓存多久、能不能缓存、需要什么条件下才能复用"。常见的响应头包括Cache-Control、Expires、Last-Modified、ETag等。
这里有个很重要的认知:缓存策略不是写在前端代码里的,而是由服务器通过响应头"下发"的。前端的性能优化,很多时候其实是在和后端、运维协作,把响应头配置好。这也是前端开发skills中经常被忽略、但在实际工作中极其实用的能力。
2.4 各层缓存的职责划分
| 缓存层级 | 作用范围 | 主要目标 | 关键控制手段 |
|---|---|---|---|
| 浏览器缓存 | 单个用户,单个浏览器 | 减少重复访问的加载时间 | Cache-Control、Expires、ETag、Last-Modified |
| CDN缓存 | 某个区域的全体用户 | 降低源站压力,加速跨地域访问 | CDN控制台配置、源站响应头 |
| 源站服务器 | 全部用户 | 生成正确的响应头,保证资源正确性 | Nginx/中间件配置、构建工具产物 |
理解了这个分层模型之后,我们再去谈具体的配置,就不会像无头苍蝇一样乱试了。接下来重点讲前端性能优化中最核心、也最容易被配错的HTTP缓存机制。
3. HTTP缓存深度拆解:强缓存与协商缓存的底层逻辑
HTTP缓存是整个前端缓存体系的基石。它的核心思路是:通过一组响应头,让浏览器和CDN判断"这个资源是否还能继续用"。HTTP缓存分两种模式:强缓存(也叫本地缓存)和协商缓存(也叫对比缓存)。两者最大的区别是:强缓存完全不需要发请求,协商缓存则需要发一个"问一问"的请求。
3.1 强缓存:不发请求直接用的底气
强缓存的意思是,浏览器判断缓存还在有效期内,就直接从本地读取,网络请求都不会发出。控制强缓存的响应头有两个:Cache-Control(HTTP/1.1)和Expires(HTTP/1.0)。
Cache-Control的常用取值如下:
max-age=31536000:资源被缓存,且在指定秒数内直接复用,不发起请求。31536000秒等于1年,这是静态资源最常用的配置。public:允许浏览器和CDN节点缓存,适合公开的静态资源。private:只允许浏览器缓存,CDN不允许缓存,适合包含用户私密信息的响应。no-cache:这个名字很容易误导人,它的意思不是不缓存,而是"使用缓存前必须先问源站验证一下"——也就是说,它会把缓存强制降级为协商缓存模式。no-store:完全不缓存,每次都必须完整下载,适合包含敏感信息的页面。
Expires是HTTP/1.0时代的老方案,它指定一个绝对过期时间(比如Expires: Wed, 21 Oct 2026 07:28:00 GMT)。问题在于:它是服务器时间,如果客户端本地时间和服务器不一致,缓存判断就可能出错。所以现代项目基本都推荐用Cache-Control的max-age(相对时间),并且要把Expires和Cache-Control同时设置时,Cache-Control的优先级更高。
3.2 协商缓存:发一个轻量请求,确认还能不能用
当强缓存过期了,或者响应头是no-cache,浏览器会带着上次缓存的标识(ETag或Last-Modified)向服务器发一个验证请求。服务器判断资源没变,就返回304状态码(Not Modified),响应体为空,浏览器继续使用本地缓存;如果变了,
