HTTP 强缓存 和 协商缓存 (浏览器缓存)

HTTP 缓存(也称 浏览器缓存)分为两大类:强缓存协商缓存。强缓存优先于协商缓存,强缓存命中时,浏览器直接使用本地副本,不会与服务器通信;若强缓存失效,则进入协商缓存阶段,向服务器验证资源是否更新。

HTTP 缓存特点

  • 浏览器自动处理,无需 JS 干预。
  • 遵循强缓存协商缓存规则。
  • 存储位置:内存缓存(memory cache)和磁盘缓存(disk cache),由浏览器自行决定。
  • 访问方式:浏览器自动读取,开发者无法通过 JS 直接读取缓存内容(只能通过 DevTools 查看)。

强缓存

强缓存是指浏览器直接从本地缓存中读取资源,而不需要向服务器发送请求。浏览器会根据缓存头信息来判断资源是否过期,如果未过期,就直接使用本地缓存的资源。

相关 HTTP 头:Expires(HTTP/1.0)和 Cache-Control(HTTP/1.1,优先级更高)。

请求头中的 Cache-Control

客户端利用 cache-control 向服务器或本地缓存提出要求 ,控制请求的行为(例如强制验证、限制使用过期缓存等)。不会直接"缓存"任何内容,而是告诉缓存如何处理已有的缓存或如何从服务器获取资源。

  • Cache-Control: no-cache(要求服务器不要返回缓存副本,必须重新验证)。
  • max-age=0 等同于 no-cache
  • max-age = s, 客户端愿意接受的响应的最大生存时间(秒),超过则重新请求。
  • max-stale = s, 客户端愿意接受过期缓存,过期时间不超过指定秒数(若不指定,任意过期也可接受)。
  • min-fresh = s, 客户端要求缓存必须还有至少 s 秒的新鲜时间。
  • Cache-Control: only-if-cached(若本地无缓存则返回 504)。客户端只接受已缓存的响应,不向源服务器发起网络请求。

响应头中的 Cache-Control

  1. cache-control:public, 响应可以被任何缓存(包括 CDN、代理)缓存。
  2. private, 响应只能被终端浏览器缓存,不允许中间代理缓存(适用于用户个人数据)。
  3. no-store, 绝对禁止缓存(既不存磁盘也不存内存),每次都必须请求原始数据。
  4. no-cache, 缓存必须在使用前向源服务器验证(即使本地有副本也必须重新请求确认)。 5. max-age = s, 响应的最大新鲜时间(秒),从生成时间起算。 6. s-maxage = s, 针对共享缓存(如 CDN)的最大新鲜时间,优先级高于 max-age。
  5. must-revalidate, 缓存过期后必须向源服务器验证,不允许使用过期缓存。
  6. proxy-revalidate, 与 must-revalidate 类似,但仅适用于共享缓存。
  7. stale-while-revalidate = s, 缓存过期后在指定秒数内可继续返回过期内容,同时后台异步重新验证。
  8. stale-if-error = s ,当源服务器出错时,可在指定秒数内使用过期缓存。
  9. immutable, 资源不会改变,可永久缓存(不验证),适合静态资源(如版本化的 CSS/JS)。
  10. no-transform, 禁止中间代理修改响应内容(如压缩、图片格式转换)。

示例 浏览器开启禁用缓存

GET 请求http://localhost:5177/api/home

示例 no-store 完全禁止任何形式的缓存

  • 客户端(浏览器)不会将资源写入磁盘或内存缓存。
  • 每次请求 :即使用户下次访问相同 URL,浏览器也必须重新向服务器请求完整资源,不会使用任何本地副本。

示例 no-cache

可以缓存,但每次使用前必须向服务器验证(协商缓存),不能直接使用强缓存。

示例 max-age=<seconds>

资源被视为"新鲜"的时长(从响应生成时刻起),单位秒。

example1 max-age = 0 / 同 no-cache

首次请求和二次请求

首次响应码返回 200, 二次返回 304 ,其中 304 响应的 Size 通常远小于 200 响应的 Size。

304 Not Modified :服务器返回仅包含响应头没有响应体。它只是告诉客户端:"你请求的资源自上次缓存以来未发生变化,请直接使用本地的缓存副本。"

首次请求

二次请求

服务器会返回 304?当客户端发起条件请求时(例如带有 If-Modified-SinceIf-None-Match 头部),服务器检查资源是否发生变化:

  • 如果未变化 → 返回 304,无响应体,客户端使用本地缓存。
  • 如果已变化 → 返回 200,同时发送新的完整资源。

example2 max-age = 60 有效期 60 秒

首次请求和二次请求 、过期后的第三次请求

当浏览器从磁盘缓存(disk cache)中直接读取资源并返回 200 状态码时,意味着该请求没有实际发起网络连接。因此:

  • 没有 TCP 连接,自然也就没有 Connection ID(连接标识)。
  • 浏览器开发者工具中 Network 面板的 Connection ID 列会显示为空

首次请求

响应指令 cache-control:public, 响应可以被任何缓存(包括 CDN、代理)缓存。

有效期内 二次请求

过期后 请求

响应头 Expires

Expires响应头(Response Header),由服务器在响应中发送给客户端,用于指示资源的过期时间。告诉浏览器在此绝对时间之前可以直接使用本地缓存,无需重新请求。

js 复制代码
  app.get("/api/logo-1", async (_, res) => {
    // 设置 Expires 为 60 秒后
    const expiresAt = new Date(Date.now() + 60 * 1000);
    res.setHeader("Expires", expiresAt.toUTCString());
    const file = await readFile(`${process.cwd()}/src/source/logo.png`, {});
    res.send(file);
  });

过期后请求

协商缓存

协商缓存是指浏览器在使用本地缓存之前,会先向服务器发送一个请求,询问服务器该资源是否有更新。如果服务器返回资源未更新的信息,浏览器就使用本地缓存;否则,就从服务器获取最新的资源。

强缓存失效后,浏览器向服务器发送请求,附上资源的"验证标记",由服务器判断资源是否更新。

  • 若资源未更新 → 服务器返回 304 Not Modified,无响应体,浏览器继续使用旧缓存。
  • 若资源已更新 → 服务器返回 200 OK 和新资源。
响应头 请求头(浏览器后续携带) 说明
Last-Modified If-Modified-Since 基于修改时间验证
ETag If-None-Match 基于唯一标识(优先级高于修改时间)

浏览器会自动在满足条件时加上协商缓存所需的请求头(If-None-Match / If-Modified-Since),这是浏览器的默认行为,无需开发者手动干预。

示例 ETag / If-None-Match HTTP/1.1

首次请求 GET 请求 http://localhost:5177/api/home

服务器返回资源,并附带 ETag 响应头

再次发起 GET 请求 http://localhost:5177/api/home

浏览器携带 If-None-Match 请求头,值为之前 响应头返回 Etag。

资源未变,客户端使用缓存。返回 304 Not Modified

示例 查看时间

GET 请求 http://localhost:5177/api/user

  • Queuing 1.39 ms, 请求在浏览器队列中等待。可能原因:有更高优先级的请求正在处理,或浏览器对同一域名并发连接数有限(HTTP/1.1 通常 6 个)。此值很小,正常。
  • Stalled 2.12 ms, 请求已被创建但尚未发出,等待可用的网络连接。
  • DNS Lookup 54 µs (0.054 ms), 域名解析时间。极短,说明 DNS 已被缓存(或主机名在本地 hosts 文件中),性能很好。
  • Initial connection 0.78 ms, TCP 握手(+ TLS 握手,如果是 HTTPS)时间。非常快,表明与服务器物理距离近或连接被复用(Keep-Alive)。注意:如果使用了 HTTPS,这里会包含 TLS 开销
  • Request sent 0.12 ms, 发送 HTTP 请求数据的时间。
  • Waiting (TTFB) 34.15 ms, Time To First Byte,从发送完请求到接收到服务器响应的第一个字节。
  • Content Download 0.45 ms, 从服务器下载响应体数据的时间。

Last-Modified / If-Modified-Since HTTP/1.0

浏览器在需要验证缓存时,会自动 根据上次服务器响应的 Last-Modified 值生成 If-Modified-Since 请求头,无需开发者手动编写代码。这是 HTTP 缓存机制的标准行为。

相关推荐
lunzi_08264 小时前
《图解HTTP》--第6章-HTTP首部
网络·网络协议·http
BINGCHN4 小时前
CVE-2026-49975(HTTP/2 Bomb 远程拒绝服务漏洞)
网络·网络协议·http·cve
无风听海5 小时前
深入解析 ASP.NET Core 中的 Request.Cookies:从 HTTP 协议到加密存储与执行时序
后端·http·asp.net
Geoking.7 小时前
SSH 一断 Node 服务就挂?排查与解决方案记录
运维·node.js·ssh
jike88ai7 小时前
Windows版Claude Code安装与API对接教程(附常见问题解决)
windows·gpt·node.js·claude·claudecode·88api
开发者联盟league7 小时前
docker登录失败解决方法。http: server gave HTTP response to HTTPS client
http·docker·https
m0_535817558 小时前
Mac下Claude Code完整配置指南:API中转+环境变量设置一步到位
gpt·macos·node.js·api·claude·claudecode·88api
米丘8 小时前
HTTP 传输层 TCP 三次握手 / 四次挥手
前端·网络协议·http
码农阿豪8 小时前
Node.js 连接金仓数据库踩坑记(上篇):环境搭建与基础操作
数据库·node.js