前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • HTTP

    • 基础篇

      • HTTP的前世今生
      • HTTP是什么
      • HTTP世界全览
      • HTTP分层
      • 键入网址到回车发生什么
      • HTTP报文是什么样子的
      • 理解请求方法
      • URI
      • 响应状态码
      • HTTP有哪些特点
      • HTTP优缺点
      • HTTP的实体数据
      • HTTP传输大文件
      • HTTP的连接管理
      • HTTP的重定向
      • HTTP的Cookie机制
      • HTTP的缓存控制
      • HTTP的代理服务
      • HTTP的缓存代理
    • 高级篇

    • 扩展篇

  • 浏览器相关

  • 计算机基础

# HTTP的缓存控制

30 秒速记

  • 缓存两条路:还新鲜就直接用(强缓存),过期了带着版本号去问服务器(协商缓存),没变就回 304
  • 服务器用 Cache-Control 定规矩:max-age 定保鲜期,起点是响应生成时刻,路上耽误的时间也算
  • no-store 不许存;no-cache 能存但每次用之前都要问;must-revalidate 过期了才必须问
  • 协商靠验证器:Last-Modified 配 If-Modified-Since,ETag 配 If-None-Match,ETag 更精确
  • 刷新会改请求:普通刷新主文档带 max-age=0 去验证,强制刷新带 no-cache 且不带验证头,直接拿 200

HTTP 缓存就两步:东西还新鲜就直接用,过期了就带着版本号去问服务器「我这份还能用吗」。 新鲜度由服务器在 Cache-Control 里定,max-age=3600 就是一小时内不用问;过期后浏览器带上 If-None-Match 或 If-Modified-Since 去验证,没变服务器回个 304,不传正文。最容易搞混的是 no-cache,它不是不缓存,是每次用之前都要先问一下,真不让存的是 no-store。项目里最常见的配法是:带 hash 的静态资源 max-age=31536000, immutable,HTML 用 no-cache。

面试官追问

追问 1静态资源到底要不要用 ETag?
参考回答

看资源怎么命名。文件名带内容 hash 的,配一年 max-age 加 immutable,根本不会走到协商,ETag 有没有无所谓;HTML 这种地址不变、内容会变的,就要靠 ETag 或 Last-Modified 做协商缓存。

追问 2发版后有用户一直看到旧页面,最可能是哪里配错了?
参考回答

先看 HTML 的响应头。HTML 被设了长 max-age,浏览器就一直用旧 HTML,旧 HTML 里引的也是旧 JS。HTML 改成 no-cache,静态资源靠文件名 hash 更新。

追问 3浏览器、代理、CDN 的缓存行为都一样吗?
参考回答

不一样。private 只允许浏览器存,CDN 不能存;s-maxage 只给共享缓存用,会覆盖 max-age;CDN 自己还有控制台里的缓存规则,可能直接无视源站的头。排查时三层要分开看。

追问 4服务器什么缓存头都不给,浏览器就不缓存了吗?
参考回答

会缓存。有 Last-Modified 时浏览器走启发式缓存,一般按 (Date - Last-Modified) × 10% 算保鲜期,一个三个月没改的文件可能被缓存好几天。所以别不写,要明确写出来。

# 服务器的缓存控制

30 秒速记

  • 流程:浏览器没缓存 → 请求 → 服务器响应并用 Cache-Control 标保鲜期 → 浏览器存起来下次复用
  • max-age 是存活时间,从响应生成时刻(Date)算起,链路和中间缓存里待的时间也扣
  • no-store = 西瓜不许进冰箱;no-cache = 能进冰箱,吃前先问超市;must-revalidate = 保鲜期内随便吃,过期了必须问
  • 其他常用:private 只许浏览器存,public 共享缓存也能存,s-maxage 专给 CDN,immutable 表示有效期内连刷新都不用验证
  • 实战配法:带 hash 的资源 max-age=31536000, immutable,HTML 用 no-cache,含个人数据的接口 private 或 no-store

服务器通过 Cache-Control 告诉浏览器「这份东西能存多久、存了之后怎么用」。 最常用的是 max-age,单位是秒,注意它从响应在服务器生成的那一刻开始算,路上走了 4 秒,max-age=5 到浏览器手里就只剩 1 秒。no-store、no-cache、must-revalidate 名字像,意思差很远:no-store 是压根不存;no-cache 是存着但每次用都要先找服务器确认;must-revalidate 是保鲜期内直接用,过期后必须确认,不许拿过期的凑合。实际配置我按资源类型分:带 hash 的长缓存,HTML 走 no-cache,用户私人数据 private。

为了更好地说明缓存的运行机制,下面我用“生鲜速递”作为比喻,看看缓存是如何工作的。

夏天到了,天气很热。你想吃西瓜消暑,于是打开冰箱,但很不巧,冰箱是空的。不过没事,现在物流很发达,给生鲜超市打个电话,不一会儿,就给你送来一个 8 斤的沙瓤大西瓜,上面还贴着标签:“保鲜期 5 天”。好了,你把它放进冰箱,想吃的时候随时拿出来。

在这个场景里,“生鲜超市”就是 Web 服务器,“你”就是浏览器,“冰箱”就是浏览器内部的缓存。整个流程翻译成 HTTP 就是

  • 浏览器发现缓存无数据,于是发送请求,向服务器获取资源;
  • 服务器响应请求,返回资源,同时标记资源的有效期;
  • 浏览器缓存资源,等待下次重用。

服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。”

你可能要问了,让浏览器直接缓存数据就好了,为什么要加个有效期呢?

这是因为网络上的数据随时都在变化,不能保证它稍后的一段时间还是原来的样子。就像生鲜超市给你快递的西瓜,只有 5 天的保鲜期,过了这个期限最好还是别吃,不然可能会闹肚子。

“Cache-Control”字段里的“max-age”和上一讲里 Cookie 有点像,都是标记资源的有效期。

但我必须提醒你注意,这里的 max-age 是“生存时间”(又叫“新鲜度”“缓存寿命”,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。

比如,服务器设定“max-age=5”,但因为网络质量很糟糕,等浏览器收到响应报文已经过去了 4 秒,那么这个资源在客户端就最多能够再存 1 秒钟,之后就会失效。

“max-age”是 HTTP 缓存控制最常用的属性,此外在响应报文里还可以用其他的属性来更精确地指示浏览器应该如何使用缓存

  • no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
  • no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;
  • must-revalidate:又是一个和 no_cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。

听的有点糊涂吧。没关系,我拿生鲜速递来举例说明一下:

  • no_store:买来的西瓜不允许放进冰箱,要么立刻吃,要么立刻扔掉;
  • no_cache:可以放进冰箱,但吃之前必须问超市有没有更新鲜的,有就吃超市里的;
  • must-revalidate:可以放进冰箱,保鲜期内可以吃,过期了就要问超市让不让吃

把服务器的缓存控制策略画了一个流程图,对照着它你就可以在今后的后台开发里明确“Cache-Control”的用法了

原理拆解: 响应进入浏览器缓存或共享缓存后,缓存会根据 Date、已驻留时间和 max-age 计算当前年龄。年龄小于新鲜度寿命时可直接复用;超过寿命只代表响应已经陈旧,并不等于缓存条目被删除。陈旧响应若带有 ETag 或 Last-Modified,通常可以发起条件请求:资源未变化时服务器返回 304 Not Modified,缓存继续使用原响应体;发生变化时返回新的 200 响应和实体。

最小验证: 输入为同一路径的两次访问,第一次响应允许缓存 30 秒并提供验证器;要验证的结论是新鲜期内可复用,过期后必须验证,而不是无条件重新下载正文。

GET /app.js HTTP/1.1
Host: example.test

HTTP/1.1 200 OK
Date: Mon, 17 Aug 2026 08:00:00 GMT
Cache-Control: max-age=30, must-revalidate
ETag: "app-v7"
Content-Type: application/javascript
Content-Length: 18

console.log("v7");

GET /app.js HTTP/1.1
Host: example.test
If-None-Match: "app-v7"

HTTP/1.1 304 Not Modified
Date: Mon, 17 Aug 2026 08:00:31 GMT
ETag: "app-v7"
Cache-Control: max-age=30, must-revalidate
@前端进阶之旅: 代码已经复制到剪贴板

关键行是 ETag 与 If-None-Match:预期第二次交换没有响应体,说明验证成功后复用了已存实体。如果资源已更新,预期应为 200 OK 和新正文;若没有验证器,过期后通常只能重新获取完整响应。

边界与排查: no-store 才是禁止存储;no-cache 允许存储,但正常使用前必须验证。must-revalidate 只约束陈旧响应,新鲜期内仍可直接复用。响应还可能经过代理或 CDN,因此排查时应同时查看 Age、Date、Cache-Control

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • HTTP

    • 基础篇

      • HTTP的前世今生
      • HTTP是什么
      • HTTP世界全览
      • HTTP分层
      • 键入网址到回车发生什么
      • HTTP报文是什么样子的
      • 理解请求方法
      • URI
      • 响应状态码
      • HTTP有哪些特点
      • HTTP优缺点
      • HTTP的实体数据
      • HTTP传输大文件
      • HTTP的连接管理
      • HTTP的重定向
      • HTTP的Cookie机制
      • HTTP的缓存控制
        • 服务器的缓存控制
        • 客户端的缓存控制
        • 条件请求
        • 小结
      • HTTP的代理服务
      • HTTP的缓存代理
    • 高级篇

    • 扩展篇

  • 浏览器相关

  • 计算机基础

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

HTTP的缓存控制|HTTP协议篇

# HTTP的缓存控制

30 秒速记

  • 缓存两条路:还新鲜就直接用(强缓存),过期了带着版本号去问服务器(协商缓存),没变就回 304
  • 服务器用 Cache-Control 定规矩:max-age 定保鲜期,起点是响应生成时刻,路上耽误的时间也算
  • no-store 不许存;no-cache 能存但每次用之前都要问;must-revalidate 过期了才必须问
  • 协商靠验证器:Last-Modified 配 If-Modified-Since,ETag 配 If-None-Match,ETag 更精确
  • 刷新会改请求:普通刷新主文档带 max-age=0 去验证,强制刷新带 no-cache 且不带验证头,直接拿 200

HTTP 缓存就两步:东西还新鲜就直接用,过期了就带着版本号去问服务器「我这份还能用吗」。 新鲜度由服务器在 Cache-Control 里定,max-age=3600 就是一小时内不用问;过期后浏览器带上 If-None-Match 或 If-Modified-Since 去验证,没变服务器回个 304,不传正文。最容易搞混的是 no-cache,它不是不缓存,是每次用之前都要先问一下,真不让存的是 no-store。项目里最常见的配法是:带 hash 的静态资源 max-age=31536000, immutable,HTML 用 no-cache。

面试官追问

追问 1静态资源到底要不要用 ETag?
参考回答

看资源怎么命名。文件名带内容 hash 的,配一年 max-age 加 immutable,根本不会走到协商,ETag 有没有无所谓;HTML 这种地址不变、内容会变的,就要靠 ETag 或 Last-Modified 做协商缓存。

追问 2发版后有用户一直看到旧页面,最可能是哪里配错了?
参考回答

先看 HTML 的响应头。HTML 被设了长 max-age,浏览器就一直用旧 HTML,旧 HTML 里引的也是旧 JS。HTML 改成 no-cache,静态资源靠文件名 hash 更新。

追问 3浏览器、代理、CDN 的缓存行为都一样吗?
参考回答

不一样。private 只允许浏览器存,CDN 不能存;s-maxage 只给共享缓存用,会覆盖 max-age;CDN 自己还有控制台里的缓存规则,可能直接无视源站的头。排查时三层要分开看。

追问 4服务器什么缓存头都不给,浏览器就不缓存了吗?
参考回答

会缓存。有 Last-Modified 时浏览器走启发式缓存,一般按 (Date - Last-Modified) × 10% 算保鲜期,一个三个月没改的文件可能被缓存好几天。所以别不写,要明确写出来。

# 服务器的缓存控制

30 秒速记

  • 流程:浏览器没缓存 → 请求 → 服务器响应并用 Cache-Control 标保鲜期 → 浏览器存起来下次复用
  • max-age 是存活时间,从响应生成时刻(Date)算起,链路和中间缓存里待的时间也扣
  • no-store = 西瓜不许进冰箱;no-cache = 能进冰箱,吃前先问超市;must-revalidate = 保鲜期内随便吃,过期了必须问
  • 其他常用:private 只许浏览器存,public 共享缓存也能存,s-maxage 专给 CDN,immutable 表示有效期内连刷新都不用验证
  • 实战配法:带 hash 的资源 max-age=31536000, immutable,HTML 用 no-cache,含个人数据的接口 private 或 no-store

服务器通过 Cache-Control 告诉浏览器「这份东西能存多久、存了之后怎么用」。 最常用的是 max-age,单位是秒,注意它从响应在服务器生成的那一刻开始算,路上走了 4 秒,max-age=5 到浏览器手里就只剩 1 秒。no-store、no-cache、must-revalidate 名字像,意思差很远:no-store 是压根不存;no-cache 是存着但每次用都要先找服务器确认;must-revalidate 是保鲜期内直接用,过期后必须确认,不许拿过期的凑合。实际配置我按资源类型分:带 hash 的长缓存,HTML 走 no-cache,用户私人数据 private。

为了更好地说明缓存的运行机制,下面我用“生鲜速递”作为比喻,看看缓存是如何工作的。

夏天到了,天气很热。你想吃西瓜消暑,于是打开冰箱,但很不巧,冰箱是空的。不过没事,现在物流很发达,给生鲜超市打个电话,不一会儿,就给你送来一个 8 斤的沙瓤大西瓜,上面还贴着标签:“保鲜期 5 天”。好了,你把它放进冰箱,想吃的时候随时拿出来。

在这个场景里,“生鲜超市”就是 Web 服务器,“你”就是浏览器,“冰箱”就是浏览器内部的缓存。整个流程翻译成 HTTP 就是

  • 浏览器发现缓存无数据,于是发送请求,向服务器获取资源;
  • 服务器响应请求,返回资源,同时标记资源的有效期;
  • 浏览器缓存资源,等待下次重用。

服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。”

你可能要问了,让浏览器直接缓存数据就好了,为什么要加个有效期呢?

这是因为网络上的数据随时都在变化,不能保证它稍后的一段时间还是原来的样子。就像生鲜超市给你快递的西瓜,只有 5 天的保鲜期,过了这个期限最好还是别吃,不然可能会闹肚子。

“Cache-Control”字段里的“max-age”和上一讲里 Cookie 有点像,都是标记资源的有效期。

但我必须提醒你注意,这里的 max-age 是“生存时间”(又叫“新鲜度”“缓存寿命”,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。

比如,服务器设定“max-age=5”,但因为网络质量很糟糕,等浏览器收到响应报文已经过去了 4 秒,那么这个资源在客户端就最多能够再存 1 秒钟,之后就会失效。

“max-age”是 HTTP 缓存控制最常用的属性,此外在响应报文里还可以用其他的属性来更精确地指示浏览器应该如何使用缓存

  • no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
  • no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;
  • must-revalidate:又是一个和 no_cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。

听的有点糊涂吧。没关系,我拿生鲜速递来举例说明一下:

  • no_store:买来的西瓜不允许放进冰箱,要么立刻吃,要么立刻扔掉;
  • no_cache:可以放进冰箱,但吃之前必须问超市有没有更新鲜的,有就吃超市里的;
  • must-revalidate:可以放进冰箱,保鲜期内可以吃,过期了就要问超市让不让吃

把服务器的缓存控制策略画了一个流程图,对照着它你就可以在今后的后台开发里明确“Cache-Control”的用法了

原理拆解: 响应进入浏览器缓存或共享缓存后,缓存会根据 Date、已驻留时间和 max-age 计算当前年龄。年龄小于新鲜度寿命时可直接复用;超过寿命只代表响应已经陈旧,并不等于缓存条目被删除。陈旧响应若带有 ETag 或 Last-Modified,通常可以发起条件请求:资源未变化时服务器返回 304 Not Modified,缓存继续使用原响应体;发生变化时返回新的 200 响应和实体。

最小验证: 输入为同一路径的两次访问,第一次响应允许缓存 30 秒并提供验证器;要验证的结论是新鲜期内可复用,过期后必须验证,而不是无条件重新下载正文。

GET /app.js HTTP/1.1
Host: example.test

HTTP/1.1 200 OK
Date: Mon, 17 Aug 2026 08:00:00 GMT
Cache-Control: max-age=30, must-revalidate
ETag: "app-v7"
Content-Type: application/javascript
Content-Length: 18

console.log("v7");

GET /app.js HTTP/1.1
Host: example.test
If-None-Match: "app-v7"

HTTP/1.1 304 Not Modified
Date: Mon, 17 Aug 2026 08:00:31 GMT
ETag: "app-v7"
Cache-Control: max-age=30, must-revalidate
@前端进阶之旅: 代码已经复制到剪贴板

关键行是 ETag 与 If-None-Match:预期第二次交换没有响应体,说明验证成功后复用了已存实体。如果资源已更新,预期应为 200 OK 和新正文;若没有验证器,过期后通常只能重新获取完整响应。

边界与排查: no-store 才是禁止存储;no-cache 允许存储,但正常使用前必须验证。must-revalidate 只约束陈旧响应,新鲜期内仍可直接复用。响应还可能经过代理或 CDN,因此排查时应同时查看 Age、Date、Cache-Control、验证器和实际状态码;仅看浏览器显示的“from cache”不足以判断缓存策略由哪一层执行。

面试官追问

追问 1Cache-Control: no-cache 是不是就不缓存了?
参考回答

不是,浏览器照样存,只是每次用之前都要发个条件请求去确认,没变就回 304,省的是正文传输。真不让存要写 no-store。

追问 2用户订单页接口被 CDN 缓存了,A 用户看到了 B 的订单,头该怎么配?
参考回答

带个人数据的响应要写 Cache-Control: private 或者干脆 no-store,共享缓存就不会存。再检查 CDN 控制台是不是配了按后缀强制缓存的规则,那个会无视源站的头。

追问 3max-age=600 和 s-maxage=60 同时写是什么效果?
参考回答

浏览器按 600 秒缓存,CDN 这类共享缓存按 60 秒。适合想让 CDN 快点回源拿新内容,又让用户本地多缓存一会的场景。

追问 4max-age 过期了是不是就要重新下载整个文件?
参考回答

不是。过期只是变成「需要验证」,浏览器带着 ETag 去问,没变回 304,正文直接用本地的。只有服务器说变了才回 200 和新内容。

追问 5immutable 解决的是什么问题?
参考回答

用户点刷新时,浏览器会把还没过期的资源也拿去验证,产生一堆 304。加了 immutable 就是告诉浏览器「有效期内这文件绝对不会变」,刷新也直接用缓存,适合带 hash 的文件。

# 客户端的缓存控制

← HTTP的Cookie机制HTTP的代理服务 →

fe