HTTP 缓存完全指南:强缓存、协商缓存与 CDN 缓存策略全解析

Web/SEO

HTTP 缓存:性能优化的第一道防线

合理配置缓存,可以让二次访问零网络请求,加载时间从秒级降到毫秒级。

缓存类型 机制 是否请求服务器 速度
强缓存 Cache-Control / Expires 否 最快(0ms)
协商缓存 ETag / Last-Modified 是(条件请求) 快(304 响应)
无缓存 no-cache / no-store 是(完整请求) 最慢

一、强缓存

Cache-Control 指令

Cache-Control: max-age=31536000, immutable
指令 作用 适用场景
max-age=N 缓存 N 秒 静态资源
s-maxage=N CDN 缓存 N 秒(覆盖 max-age) CDN 场景
immutable 即使过期也不重新验证 带哈希的文件名
public 允许中间缓存 CDN
private 仅浏览器缓存 用户个性化内容
no-cache 每次使用前必须验证 HTML 页面
no-store 完全不缓存 敏感数据
must-revalidate 过期后必须验证 API 响应
stale-while-revalidate=N 过期后先返回旧值,后台刷新 渐进更新

Expires(旧标准)

Expires: Thu, 02 Jun 2027 00:00:00 GMT

优先级:Cache-Control: max-age > Expires。


二、协商缓存

ETag / If-None-Match

# 首次请求
GET /api/data
→ 200 OK
  ETag: "abc123"
  Cache-Control: max-age=0

# 再次请求(带条件)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified  (无响应体,节省带宽)

Last-Modified / If-Modified-Since

# 首次请求
GET /style.css
→ 200 OK
  Last-Modified: Mon, 01 Jun 2026 12:00:00 GMT

# 再次请求
GET /style.css
If-Modified-Since: Mon, 01 Jun 2026 12:00:00 GMT
→ 304 Not Modified

ETag vs Last-Modified

维度 ETag Last-Modified
精度 字节级 秒级
性能 需计算哈希 直接读文件时间
可靠性 高 低(1 秒内多次修改丢失)
分布式 需保证哈希一致 天然一致

推荐:优先使用 ETag。


三、缓存策略矩阵

HTML 页面

Cache-Control: no-cache

HTML 是入口文件,必须每次验证,确保用户拿到最新版本。

带 Hash 的静态资源

Cache-Control: max-age=31536000, immutable

文件名含 Hash(如 app.abc123.js),内容变化时 Hash 变化 → URL 变化 → 新请求。旧 URL 永远不会变,可以缓存一年且标记 immutable。

不带 Hash 的静态资源

Cache-Control: max-age=86400, stale-while-revalidate=604800

过期后先返回旧值,后台静默刷新,用户无感知。

API 响应

Cache-Control: private, max-age=0, must-revalidate

API 响应通常含用户数据,private 禁止 CDN 缓存,must-revalidate 确保过期后不返回旧值。


四、Vary 头:缓存键的扩展

Vary: Accept-Encoding, Accept-Language

Vary 告诉缓存:除了 URL,还要根据这些请求头的值区分缓存版本。

Vary 值 含义 场景
Accept-Encoding 按压缩方式缓存 gzip vs br
Accept-Language 按语言缓存 多语言站点
Cookie 按 Cookie 缓存 个性化页面
* 每个请求独立缓存 等于不缓存

注意:Vary: Cookie 会导致缓存命中率极低,慎用。


五、CDN 缓存策略

回源控制

# CDN 缓存 1 天,浏览器缓存 5 分钟
Cache-Control: public, s-maxage=86400, max-age=300
场景 s-maxage max-age 说明
静态资源 31536000 31536000 CDN 和浏览器都长缓存
API 响应 300 0 CDN 缓存 5 分钟,浏览器不缓存
HTML 60 0 CDN 缓存 1 分钟,浏览器不缓存
个性化内容 — 0 CDN 不缓存,浏览器不缓存

CDN 缓存刷新

方式 速度 适用
URL 预热 快 新内容上线
全量刷新 慢 紧急修复
目录刷新 中 批量更新
Cache-Control: no-cache 自动 API 场景

Stale-While-Revalidate 实战

Cache-Control: max-age=60, stale-while-revalidate=86400
第 0 秒:请求 → 缓存 MISS → 回源 → 返回新鲜值
第 30 秒:请求 → 缓存 HIT → 返回缓存值
第 90 秒:请求 → 缓存 STALE → 立即返回旧值 + 后台回源
第 91 秒:后台回源完成 → 缓存更新
第 120 秒:请求 → 缓存 HIT → 返回新值

效果:用户永远不等待回源,后台静默更新。


六、缓存破坏策略

内容哈希(推荐)

<!-- 构建时生成 Hash -->
<script src="/app.abc123.js"></script>
<link href="/style.def456.css">
// webpack 配置
output: {
  filename: '[name].[contenthash:8].js',
}

版本号查询参数

<!-- 简单但 CDN 可能不缓存 ?v= 参数 -->
<script src="/app.js?v=2.0.0"></script>

问题:某些 CDN 忽略查询参数,导致缓存不更新。

时间戳(不推荐)

<!-- 每次都变,完全破坏缓存 -->
<script src="/app.js?t=1717315200"></script>

七、Service Worker 缓存

Cache First 策略

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => cached || fetch(event.request))
  );
});

Stale While Revalidate 策略

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('dynamic').then(cache =>
      cache.match(event.request).then(cached => {
        const fetchPromise = fetch(event.request)
          .then(response => {
            cache.put(event.request, response.clone());
            return response;
          });
        return cached || fetchPromise;
      })
    )
  );
});

缓存策略选择

策略 优先来源 适用
Cache First 缓存 字体、图片
Network First 网络 API、HTML
Stale While Revalidate 缓存+后台更新 CSS、JS
Network Only 网络 非 GET 请求
Cache Only 缓存 离线页面

八、调试缓存

Chrome DevTools

  1. DevTools → Network 面板
  2. 查看 Size 列:
    • from disk cache:磁盘缓存命中
    • from memory cache:内存缓存命中
    • 实际大小:未命中
  3. 查看 Age 响应头:缓存已存在的时间
  4. 勾选 Disable cache:禁用缓存(DevTools 打开时)

curl 验证

# 查看响应头
curl -I https://example.com/style.css

# 发送条件请求
curl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# 304 表示协商缓存命中

# 模拟不同 Accept-Encoding
curl -I -H 'Accept-Encoding: br' https://example.com/style.css

九、最佳实践清单

  1. HTML:no-cache,确保入口始终最新
  2. 带 Hash 的 JS/CSS:max-age=1年 + immutable
  3. API:must-revalidate,过期后必须验证
  4. CDN:s-maxage 控制回源频率
  5. Vary: Accept-Encoding,区分压缩方式
  6. 使用 ETag 而非 Last-Modified
  7. stale-while-revalidate 实现无感更新
  8. Service Worker 补充离线能力
#HTTP缓存#Cache-Control#ETag#CDN#性能优化