HTTP缓存这块,说实话属于那种“看着简单,一用就翻车”的知识点。面试题里背得滚瓜烂熟,真到了线上资源更新不了、用户反馈页面还是旧版的时候,才发现自己根本搞不清楚强缓存和协商缓存的边界到底在哪。我做了这么多年Web开发和性能优化,自己也踩过不少坑,今天就把这套机制从头到尾扒一遍,把该说的原理、该有的实操、该避的坑一次性讲清楚。
这篇内容适合谁看?后端开发、前端开发都想得明白缓存怎么配;运维和DevOps要懂CDN和Nginx层缓存如何与业务配合;刚入门没多久的新人,也能通过这篇文章建立完整的HTTP缓存心智模型。不管你是纯浏览器端缓存,还是涉及CDN、网关层缓存,这套底层逻辑基本是通用的。
1. HTTP缓存是什么,为什么要折腾它
1.1 缓存解决的核心问题
一个用户在浏览器里输入网址、按下回车,到页面完整展示,中间要经过DNS解析、TCP连接、TLS握手、发送HTTP请求、服务器处理、返回响应、浏览器解析渲染。一个页面里几十个JS、CSS、图片资源,如果每次都重新从服务器拉一遍,这个成本极其夸张。
我做过一个真实的性能优化案例:一个后台管理系统,首屏要加载80多个静态资源,未缓存时这些资源加起来约4.5MB。开启合理的缓存策略后,第二次访问这些静态资源基本不再消耗网络流量,耗时降低到原来的十分之一左右。这就是缓存的直接价值:减少重复请求、节省带宽、降低服务端压力、大幅提升加载速度。
HTTP缓存机制实际上是一个多级体系,从浏览器缓存、CDN缓存、反向代理缓存,到服务端缓存、数据库缓存,每一层都有自己的策略和生命周期。而我们今天重点讲的强缓存和协商缓存,是其中最核心、最直接影响每个HTTP请求是否发出去的决策机制。
1.2 一个必须建立的认知:缓存不是“不请求”,而是“少请求”或“不重复传输”
很多人对缓存的理解就是“资源被存下来了,所以下次直接用”。这不完全对。缓存协议里有两个关键动作:
- 强缓存(也叫本地缓存、强制缓存):浏览器判断本地缓存未过期,直接使用,连请求都不发给服务器。
- 协商缓存(也叫对比缓存、弱缓存):浏览器不确定缓存是否还能用,于是带着缓存的标识去问服务器——“我这个版本还能用吗?”服务器说“可以用”(304),那就继续用本地缓存;服务器说“不行了,内容变了”(200),那就返回新内容。
所以核心区别在于:强缓存省请求,协商缓存省流量。一个根本不出门,一个出门但带得少。这个认知放到后面理解Header配置时会非常有用。
1.3 缓存的载体和层级
浏览器缓存通常分两层:内存缓存(memory cache)和磁盘缓存(disk cache)。内存缓存读取快,但存活时间短,浏览器关闭后基本就没了;磁盘缓存容量大、持久化,但读取相对慢一些。强缓存命中的资源可能来自memory cache,也可能来自disk cache,Chrome的Network面板里会直接显示(比如from memory cache或from disk cache)。
另外,CDN节点和反向代理(比如Nginx)也会形成缓存层。它们和浏览器缓存遵循的HTTP协议是同一套,但配置位置和生效范围不同。我们在浏览器看到的缓存策略,同样会影响到CDN层是否缓存。这也是为什么很多公司会把静态资源的Cache-Control设置为public,就是为了让CDN也能缓存一份,用户就近访问。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 强缓存:浏览器说了算,不找服务器
强缓存的核心逻辑非常“霸道”:只要我手里这份缓存还没过期,我就不问你服务器,直接用本地的。能让浏览器作出这个判断的,靠的是响应头里的两个字段:Expires和Cache-Control。
2.1 两个关键响应头:Expires和Cache-Control
Expires是HTTP/1.0时期的标准,指定一个绝对的过期时间,比如:
http复制Expires: Wed, 21 Oct 2026 07:28:00 GMT
浏览器看到这个时间还没到,就直接用缓存;超过这个时间,就认为缓存失效。
但Expires有个非常明显的缺陷:它是绝对时间,依赖客户端本地时间。如果用户电脑的时间不对(比如调快了、调慢了),或者时区混乱,缓存判断就可能出错。所以后来HTTP/1.1引入了Cache-Control,它更强大,使用相对时间,不依赖客户端时钟。比如:
http复制Cache-Control: max-age=3600
这表示从响应生成时刻起,3600秒内浏览器可以直接使用缓存。
如果两个字段同时出现,Cache-Control的优先级更高。这一点是规定,也是实际行为,只要响应头里有Cache-Control,Expires基本就是个摆设。我建议现在的新项目干脆只考虑Cache-Control,Expires了解即可,更多是历史兼容需要。
2.2 Cache-Control常用指令逐个拆解
Cache-Control这个字段支持多个指令,用逗号分隔,看起来简单,坑不少。
max-age=<seconds>:缓存有效期的相对秒数。比如max-age=86400表示一天。s-maxage=<seconds>:专门给共享缓存(如CDN、代理服务器)用的有效期,优先级高于max-age。浏览器会忽略它,这个我要特别提一下,很多人不知道它只管CDN层。public:允许所有缓存服务器缓存该响应,包括CDN、代理等。private:只允许浏览器等私有缓存存储,不允许CDN或代理缓存。适合包含用户个性化信息的响应。no-cache:这个名字非常坑。它不是“不缓存”,而是“可以缓存,但每次使用前必须向服务器验证一下是否有效”。也就是说,它强制走协商缓存,即使缓存还没过期。no-store:
