浏览器缓存策略

想象一下浏览器就像一个有记忆的快递员,它负责从网站服务器(仓库)给你取东西(网页、图片、脚本等)。缓存策略就是告诉这个快递员:“哪些东西你可以记住(缓存),下次直接给我,不用再跑仓库了?什么时候需要再去仓库确认一下东西有没有变?”

核心目标:减少不必要的网络请求,加快网页加载速度,节省带宽。

缓存策略主要分为两大类:

  1. 强缓存:浏览器“自作主张”,完全不问服务器,直接用本地缓存。
  2. 协商缓存:浏览器会问服务器:“我本地这个缓存还能用吗?东西变没变?”服务器根据情况决定是让浏览器用缓存还是给新内容。

一、强缓存:快递员自己决定用不用存货

浏览器在发起请求前,会先检查本地缓存。如果缓存有效,浏览器直接使用缓存,完全不发送请求到服务器!速度最快。

判断缓存是否有效,主要看两个 HTTP 响应头

  1. Expires(HTTP/1.0 产物)

    • 作用:告诉浏览器这个资源的“保质期”到什么时候。
    • 值:一个具体的日期时间(如 Wed, 21 Oct 2025 07:28:00 GMT)。
    • 判断逻辑:浏览器拿当前时间和 Expires 时间对比。如果当前时间早于 Expires,缓存有效,否则无效
    • 缺点:依赖客户端(浏览器)的本地时间。如果用户电脑时间不准,缓存可能提前失效或持续有效。
  2. Cache-Control(HTTP/1.1 引入,优先级高于 Expires) 作用:更强大、更灵活地控制缓存行为。 常用指令:- max-age:最重要的指令,单位是秒。告诉浏览器该资源在多少秒内是新鲜的(新鲜的就可以直接用)。如Cache-Control: max-age=3600 表示 1 小时内有效 - public:响应可以被任何中间代理(如 CDN)和浏览器缓存。适合所有人共享的资源。- private:响应只能被用户浏览器缓存,不能被中间代理缓存。适合个性化内容 - no-cache:注意!不是“不缓存”,而是“缓存了但每次都要去服务器验证”。

    • no-store:真正的不缓存。浏览器和任何中间代理都不能存储这个响应的任何部分。每次都得去服务器拿。
    • immutable:(较新)表示资源永远不会变。在新鲜期内(由 max-age 指定),即使用户刷新页面,也不会去服务器验证。适合带哈希指纹的资源(如 main.abcd1234.js)。
    • must-revalidate:一旦缓存过期(超过 max-age),必须去服务器验证,不能直接用过期缓存。

判断逻辑:浏览器计算资源被缓存的时间 + max-age 秒,看是否超过当前时间。没超过,缓存有效,直接用。

注意:

  • 浏览器开发者工具里看到的 200 (from disk cache) 或 200 (from memory cache),表示资源来自强缓存,没有向服务器发请求。
  • 强缓存失效后,浏览器会带缓存标识去服务器验证(协商缓存)或直接发新请求(取决于资源类型和设置)。

二、协商缓存:快递员先去仓库确认一下

当强缓存失效(过期了)或者响应头明确要求协商(如 Cache-Control: no-cache),浏览器就会向服务器发起一个验证请求。这个请求会带上一些关于本地缓存的信息。服务器根据这些信息判断缓存是否还能用。

关键点:协商缓存一定会发起请求,但如果缓存有效,服务器只返回一个很小的响应头(通常是 304),告诉浏览器“缓存还能用”,浏览器就直接用本地缓存。这比下载整个资源快得多。

协商缓存主要依赖两组请求头/响应头:

  1. Last-Modified / If-Modified-Since

原理:基于文件的修改时间。 流程:- 服务器第一次响应资源时,响应头加 Last-Modified,值是资源最后修改时间。

- 浏览器缓存这个时间。 - 需要验证缓存时(强缓存失效或 no-cache),浏览器请求头带 If-Modified-Since,值为上次缓存的 Last-Modified。 - 服务器比较 If-Modified-Since 和资源实际修改时间: - 没变(服务器时间 `<=` If-Modified-Since):返回 304,浏览器用本地缓存。 - 变了(服务器时间 > If-Modified-Since):返回 200 和新内容,浏览器更新缓存。

2. ETag / If-None-Match

  • 原理:基于资源的唯一标识符(类似文件“指纹”或哈希值),比 Last-Modified 更精确。

  • 为什么需要它?Last-Modified 有缺陷:

    • 精度只到秒,1 秒内多次修改无法区分。
    • 文件内容没变但修改时间变了(如 touch 文件),会导致不必要的重新下载。
    • 某些服务器无法准确获取文件修改时间。
  • 流程:

    • 服务器响应时加 ETag,值是唯一标识当前资源内容的字符串(通常是内容哈希)。
    • 浏览器缓存这个 ETag。
    • 验证缓存时,浏览器请求头带 If-None-Match,值为缓存的 ETag。
    • 服务器比较 If-None-Match 和当前资源 ETag:匹配(值相同):返回 304,浏览器用本地缓存;不匹配:返回 200 和新内容,浏览器更新缓存。
  • ETag 类型:

    • 强验证(ETag: "686897696a7c876b7e"):字节完全一致。
    • 弱验证(ETag: W/"686897696a7c876b7e"):允许一些非实质性改动。

优先级:

  • 服务器通常会同时提供 ETag 和 Last-Modified。
  • 浏览器验证时会同时带 If-None-Match 和 If-Modified-Since。
  • 服务器优先检查 If-None-Match(ETag),只有 ETag 不可用时才用 Last-Modified。

三、缓存位置:快递员把东西放哪儿了?

浏览器缓存有不同层级,访问速度和生命周期不同

  1. Service Worker Cache

    • 最高级控制:需开发者注册 Service Worker 并编写缓存逻辑。
    • 功能强大:可实现“缓存优先”、“网络优先”等策略,支持离线应用。
    • 持久性:除非明确删除,否则长期存在。
  2. Memory Cache(内存缓存)

    • 速度最快,直接从内存读取。
    • 容量小,生命周期短(关闭标签页或浏览器就没了)。
    • 常用于当前会话中频繁访问的资源。
  3. Disk Cache(硬盘缓存)

    • 速度较慢,需要磁盘 I/O。
    • 容量大,生命周期长(关闭浏览器后依然存在)。
    • 是缓存的主力军。
  4. Push Cache(推送缓存)

    • HTTP/2 特性,服务器主动推送的资源暂存在这里。
    • 生命周期极短,只在当前会话有效。
    • 实际应用较少。

访问顺序:浏览器查找缓存时,通常按 Service Worker → Memory Cache → Disk Cache → Push Cache 的顺序查找。找到即用,找不到才发网络请求。

四、如何设计好的缓存策略?

没有一刀切的策略,需要根据资源类型决定:

  1. 频繁变动(如 HTML 文件,API 数据)

    • Cache-Control: no-cache 或 Cache-Control: no-store(如果非常敏感)。
    • 配合 ETag 或 Last-Modified 进行协商缓存。每次都验证,但能用 304 节省带宽。
  2. 不常变动(如 JS/CSS 库、字体、图片)

    • 最佳实践:使用内容哈希,文件名中带哈希值(如 main.abcd1234.js)。内容变,文件名变。
    • 设置超长强缓存:Cache-Control: public, max-age=31536000(一年)。
    • 可加 immutable,告诉浏览器新鲜期内连刷新都不用验证。
  3. 介于两者之间(如用户头像可能会更新的图)

    • 设置中等时长强缓存:Cache-Control: public, max-age=86400(一天)。
    • 配合 ETag 或 Last-Modified。有效期内直接用,过期后去服务器验证。

总结关键点

  1. 强缓存(Expires, Cache-Control):浏览器自己判断,有效就不发请求(200 from cache)。
  2. 协商缓存(Last-Modified/If-Modified-Since, ETag/If-None-Match):浏览器发小请求问服务器,缓存有效返回 304,无效返回 200 和新内容。ETag 比 Last-Modified 可靠
  3. 缓存位置:分多层(Service Worker, Memory, Disk, Push),速度、容量、生命周期不同。
  4. 策略设计:根据资源类型选择。内容哈希 + 长缓存是静态资源的最佳实践。

常见易混淆点小结

  • no-cache 并不是“不缓存”,而是“缓存了但每次都要去服务器验证”。
  • no-store 才是完全不缓存。
  • ETagLast-Modified 更精确,但两者可配合使用。
  • Service Worker Cache 需开发者主动实现。
  • 强制刷新会跳过所有缓存,服务器一定返回新资源。

理解并合理运用浏览器缓存策略,是优化网站性能、提升用户体验的关键一步!