缓存的层次

一个网页从服务器到你眼前,可能经过多层缓存:

  • 浏览器缓存(本地磁盘/内存);
  • CDN 缓存;
  • 反向代理缓存(如 Nginx);
  • 应用内缓存。

任何一层返回了旧的副本,你看到的就是旧内容。

浏览器缓存的控制

服务器通过 HTTP 响应头告诉浏览器如何缓存:

一、强缓存

Cache-Control: max-age=3600

效果:在 3600 秒内,浏览器直接使用本地副本,不向服务器发请求。

这在浏览器开发者工具中显示为"from disk cache"或"from memory cache"。

问题:如果内容更新了,浏览器不会知道——直到缓存过期。

二、协商缓存

ETag: "abc123"
Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT

浏览器下次请求时带上:

If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT

服务器判断:

  • 内容未变 → 返回 304 Not Modified(无响应体),浏览器用本地副本;
  • 内容已变 → 返回 200 和新内容。

协商缓存每次都要发请求,但内容未变时不传输数据,节省带宽。

为什么需要"刷新"

普通刷新(F5):浏览器会发送条件请求(协商缓存),验证内容是否更新。

强制刷新(Ctrl+F5 / Cmd+Shift+R):浏览器跳过缓存,直接向服务器请求全新内容。

所以当你怀疑看到的是缓存时,强制刷新是最快的验证方法。

前端资源的缓存策略

问题:如果 JS/CSS 文件被强缓存,网站更新后用户可能一直看到旧版本。

业界标准做法:文件名哈希

app.a3f2c1d9.js
style.7b8e4f2a.css

原理:

  • 文件内容变化时,哈希值也变化 → 文件名不同 → 浏览器认为是不同的资源 → 重新下载;
  • 文件内容不变时,文件名不变 → 命中缓存。

同时对 HTML 文档本身采用 Cache-Control: no-cache(每次都协商验证),这样 HTML 更新时能及时获取,而静态资源可以放心地强缓存。

这是最可靠的方案——既享受了缓存的性能,又保证了更新的及时性。

Service Worker

现代 Web 应用可以用 Service Worker 更精细地控制缓存。

它可以:

  • 在后台预缓存资源,实现离线可用;
  • 实现自定义的缓存策略(如"优先网络,失败时回退到缓存");
  • 提供"有新版本可用"的提示。

代价:Service Worker 的缓存更新机制比较复杂。如果配置不当,用户可能长期停留在旧版本——这也是"刷新才能看到新内容"的一个常见原因。

一个排查思路

如果网站内容不更新,按顺序检查:

  1. 强制刷新试试(排除浏览器缓存);
  2. 查看响应头(确认 Cache-Control、ETag);
  3. 检查 CDN 是否缓存了 HTML(很多 CDN 默认只缓存静态资源,但如果配置了页面缓存就会有问题);
  4. 检查代理层;
  5. 确认源站的内容确实更新了。

说明:本文为 Web 缓存机制的知识整理。