浏览器缓存实战:强缓存、协商缓存,以及发版后用户还是旧页面的五个原因

举报
yd_252100366 发表于 2026/10/20 16:14:30 2026/10/20
【摘要】 ## 一、两条缓存路径浏览器请求资源时先查本地缓存,分两种命中方式:- **强缓存**:直接读本地,**不发请求**,状态码 200(from disk cache / memory cache)- **协商缓存**:发请求到服务端问"这个还能用吗",能用回 **304 Not Modified**,只回响应头不带 body两者的区别就是"要不要问服务端"。强缓存快但更新滞后,协商缓存每次都...

## 一、两条缓存路径

浏览器请求资源时先查本地缓存,分两种命中方式:

- **强缓存**:直接读本地,**不发请求**,状态码 200(from disk cache / memory cache)
- **协商缓存**:发请求到服务端问"这个还能用吗",能用回 **304 Not Modified**,只回响应头不带 body

两者的区别就是"要不要问服务端"。强缓存快但更新滞后,协商缓存每次都有一次往返但内容永远最新——所以最佳实践是**组合使用**:静态资源用强缓存 + 文件名 hash 解决更新,HTML 用协商缓存保证入口最新。

## 二、强缓存的两个头

```http
Cache-Control: max-age=31536000, immutable   // 优先级高
Expires: Wed, 21 Oct 2026 07:28:00 GMT        // HTTP/1.0 老写法,受本地时间影响
```

`Cache-Control` 优先于 `Expires`,现代项目只写前者即可。几个常用指令:

| 指令 | 含义 |
| --- | --- |
| max-age=N | 相对过期时间(秒) |
| no-cache | 不用强缓存,但可以走协商缓存(**不是不缓存**) |
| no-store | 完全不缓存,连协商都不走 |
| immutable | 有效期内绝不发请求,连刷新都不验证 |

最容易搞混的是 `no-cache` 和 `no-store`:前者意思是"每次都去服务端验证",后者才是"彻底别存"。

## 三、协商缓存的两组头

```http
ETag: "5f8a-1b2c"           // 服务端响应
If-None-Match: "5f8a-1b2c"  // 浏览器下次请求带上
```

```http
Last-Modified: Tue, 20 Oct 2026 06:00:00 GMT
If-Modified-Since: Tue, 20 Oct 2026 06:00:00 GMT
```

优先级:**ETag / If-None-Match 高于 Last-Modified / If-Modified-Since**,同时存在时以前者为准。

为什么 ETag 更可靠?因为 Last-Modified 只精确到秒,一秒内多次修改感知不到;而且它依赖文件 mtime,内容没变但 touch 了文件也会被判定为已修改。ETag 是内容指纹,更准确,代价是服务端要多算一次。

## 四、完整判断链条

1. 有强缓存且未过期 → 直接用,**不发请求**
2. 强缓存过期或用了 no-cache → 发请求,带上 If-None-Match / If-Modified-Since
3. 服务端比对 → 没变回 304(浏览器用本地副本),变了回 200 带新内容
4. 用户按 F5:跳过强缓存,走协商缓存;Ctrl + F5:全部跳过,强制拉新

## 五、Nginx 标准配置

```nginx
# HTML 入口:协商缓存,保证发版后立刻拿到新引用
location ~* \.html$ {
    add_header Cache-Control "no-cache";
}

# 带 hash 的静态资源:一年强缓存 + immutable
location ~* \.(js|css|png|jpg|svg|woff2)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}
```

核心前提是**文件名带内容 hash**(webpack/vite 默认就是这么做的)。文件内容一变 hash 就变,URL 变了自然不受旧缓存影响——这才是"长缓存 + 快速更新"能同时成立的原因。

## 六、发版后用户还是旧页面:五个原因

### 1. HTML 被强缓存了

最常见。入口 HTML 配了 max-age,用户拿到的还是旧 HTML,里面引用的还是旧 hash 的资源。**修复:HTML 一律 no-cache**。

### 2. index.html 有缓存但 JS 没更新(或反之)

典型的版本错配:新 HTML 引用 `app.a1b2.js`,但该文件因为 CDN 缓存或发布不完整返回 404/旧内容,页面白屏。排查看 Network 里失败的资源。

### 3. CDN 边缘节点没刷新

源站已更新,CDN 还在缓存旧文件。**修复:发布流程里加 CDN 刷新(目录级 purge),并确认刷新完成再放量**。

### 4. Service Worker 拦截了请求

旧 SW 缓存优先返回,页面永远拿不到新资源。**修复:更新 SW 版本号并在 activate 里清旧 cache;必要时引导用户跳过等待**。

### 5. 用户侧本地缓存顽固

浏览器磁盘缓存、代理缓存、企业网关缓存都可能。**验证方法**:无痕窗口打开、加随机 query 参数、看响应头 `age` 和 `x-cache`。

## 七、排查顺序(照这个走最快)

1. 无痕窗口打开——还旧就是服务端/CDN 问题,新了就是用户本地缓存
2. 看 HTML 响应头有没有 `Cache-Control: no-cache`
3. 看 CDN 响应头 `x-cache: HIT/MISS` 与 `age`
4. 看资源 URL 的 hash 是否变了
5. 最后才怀疑 SW 与浏览器缓存

## 八、总结

缓存策略一句话:**HTML 不缓存或协商缓存,静态资源长强缓存 + hash 文件名**。发版问题先分清"是入口旧还是资源旧",再按 HTML → CDN → SW → 本地 的顺序排查,比盲目让用户清缓存高效得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。