浏览器缓存是提升网页性能的核心机制,它通过存储资源副本(如HTML、CSS、JS、图片等),在后续请求时直接复用本地资源。
缓存的价值
减少网络请求:减少重复资源传输,降低服务器负载,节省服务器带宽加速页面加载:使用缓存时,从本地磁盘/内存读取资源比网络请求快数十倍
资源缓存的位置:
位置 (按读取速度排序)特性生命周期Memory Cache内存缓存,读取速度最快会话级,关闭浏览器标签页即失效Disk Cache磁盘缓存,容量大但速度稍慢持久存储,手动或过期才清除Service Worker可编程缓存,支持离线、拦截请求完全由代码控制缓存哪些文件、如何匹配/读取缓存,且缓存是持续性的Push Cache推送缓存,以上都没命中时,才启用会话级,关闭浏览器标签页即失效
缓存流程:
浏览器内部维护了一个 缓存存储(包括Memory Cache、Disk Cache等)
// 伪代码:浏览器缓存数据库结构
const cacheStorage = {
"https://example.com/logo.png": {
responseHeaders: {
"Cache-Control": "max-age=3600",
"ETag": "deadbeef123",
"Date": "Wed, 19 Jul 2025 08:00:00 GMT"
},
body: [图片二进制数据],
expirationTime: 1721386800000 // 2025-07-19 09:00:00
},
"https://api.com/user": {
// 另一个缓存条目...
}
}
当浏览器发起一个 HTTP 请求时,会先在 缓存存储 中查找是否有匹配的 URL
若有匹配的 URL:则根据该 缓存信息 来检查强缓存
强缓存:检查Cache-contral(是否允许使用缓存?) 和Expires(缓存是否过期?),若强缓存失效,则进入协商缓存。协商缓存:携带 缓存验证字段 向服务器发送请求,验证缓存资源是否最新
若没有匹配的 URL:则向服务器发送此请求,并根据 响应头 来存储 缓存信息
关键缓存策略
强缓存
注意:此过程不产生任何网络请求
协商缓存
读取 缓存信息:
若存在 Etag --> 准备 If-None-Match头若存在 Last-Modified --> 准备 If-Modified-Since头
发送带 验证头 的轻量请求
GET /data.json HTTP/1.1
Host: api.example.com
If-None-Match: "deadbeef123"
If-Modified-Since: Wed, 19 Jul 2025 08:00:00 GMT
服务器验证逻辑:
资源未变 --> 返回 304 Not Modified(空 body)资源已变 --> 返回 200 OK + 新数据
浏览器处理响应:
收到 304 --> 从缓存读取数据收到 200 --> 使用新数据并更新缓存
缓存相关字段
服务器响应头
字段说明示例值Cache-Control最重要的缓存控制头(HTTP/1.1)max-age=3600, publicExpires资源过期绝对时间(HTTP/1.0,已被Cache-Control取代)Expires: Wed, 21 Oct 2025 07:28:00 GMT缺点:判断是否过期时是使用本地时间,而本地时间可以修改ETag资源唯一标识符(通常是内容哈希值)ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"Last-Modified资源最后修改时间Last-Modified: Mon, 01 Jul 2024 12:00:00 GMTVary指定哪些请求头影响缓存版本Vary: User-AgentAge资源在代理缓存中已存储的时间(秒)Age: 600PragmaHTTP/1.0遗留字段(等同于Cache-Control: no-cache)Pragma: no-cache
浏览器请求头
字段说明触发条件If-None-Match携带缓存的ETag值进行验证响应头中有ETagIf-Modified-Since携带缓存的最后修改时间进行验证响应头中有Last-ModifiedCache-Control客户端可覆盖缓存行为用户强制刷新时If-Match用于更新操作时的条件请求非GET请求
核心字段解析
Cache-Control:最强大的缓存控制字段,支持以下多种指令组合
指令作用使用场景max-age=<秒>资源有效期(相对时间)max-age=3600(1小时有效)s-maxage=<秒>覆盖max-age但仅对共享缓存有效(如CDN)s-maxage=86400(CDN缓存1天)public允许任何缓存(浏览器、CDN等)公开资源:Cache-Control: public, max-age=3600private仅允许用户浏览器缓存用户私有数据:Cache-Control: private, max-age=600no-cache跳过强缓存,强制向服务器验证重要数据:Cache-Control: no-cacheno-store完全禁用缓存(不存储任何版本)敏感数据:Cache-Control: no-storemust-revalidate缓存过期后必须向服务器验证Cache-Control: max-age=3600, must-revalidate当资源过期且浏览器无法链接服务器时:普通缓存可能展示过期数据(浏览器宽容策略),但设置了must-revalidate会直接报错504 Gateway Timeout(拒绝使用过期缓存)immutable资源永不变,用户刷新也不验证哈希版本资源:Cache-Control: max-age=31536000, immutablestale-while-revalidate允许使用过期缓存同时后台更新Cache-Control: max-age=3600, stale-while-revalidate=86400stale-if-error服务器错误时使用过期缓存的时间Cache-Control: max-age=3600, stale-if-error=86400
资源的最佳缓存策略
资源类型配置策略理由频繁变动的资源Cache-Contral:no-cache直接进入协商缓存,配合 Etag 或 Last-Modified 来验证资源是否有效。 这样做虽然不能节省请求数量,但能显著减少响应数据大小不常变化的资源Cache-Control:man-age=31536000设置很大的有效期,进入强缓存。 若资源更新了,需要更改资源名称中的 hash / 版本号 等动态字符,达到更改 URL 的目的。# 以一个 nginx 配置为例
location /share {
if ($request_filename ~* ^.*[.](html|htm)$) {
#html文件不缓存
expires 0;
add_header Cache-Control "no-store";
}
if ($request_filename ~* ^.*[.](js|css|json)$) {
#不带hash的静态资源,设置协商缓存
add_header Cache-Control "no-cache";
}
if ($request_filename ~* .*.(chunk).(css|js)$) {
#匹配带hash的js css强缓存1个月
add_header Cache-Control "public, max-age=2592000";
}
if ($request_filename ~* ^.*[.](png|jpg|svg|ico)$) {
#匹配静态png jpg 资源强缓存1个月
add_header Cache-Control "public, max-age=2592000";
}
if ($request_filename ~* ^.*[.]migrate.dll.js$){
#匹配migarte的js文件 协商缓存
add_header Cache-Control "no-cache";
}
if ($request_filename ~* ^.*[.]migrate.css$){
#匹配migarte的css文件 协商缓存
add_header Cache-Control "no-cache";
}
alias "/opt/web/build";
index index.html;
try_files $uri $uri/ /share/index.html;
}
用户操作对缓存的影响
用户操作缓存行为技术细节打开网页,地址栏输入地址使用 Disk Cache ,若有则使用缓存资源,若没有则发送请求前进/后退优先使用页面快照(bfcache),不重新请求资源。整个页面从内存恢复,资源不重新加载(PageShow 事件可监听)。普通刷新(F5)跳过强缓存,触发协商缓存验证。优先使用 Memory Cache,其次使用 Disk Cache请求头带有缓存校验字段(If-Modified-Since等),服务器决定返回 304 或 200。强制刷新(Ctrl+F5)不使用缓存请求头带有Cache-Control: no-cache (为了兼容,还带了 Pragma: no-cache),服务器返回 200和最新资源。