跳到主要内容

CDN缓存策略与刷新

1. CDN 缓存层级

浏览器缓存 → CDN 边缘缓存 → CDN 中间层缓存 → 源站

每层都有独立的缓存时间:

  • 浏览器:max-age
  • CDN 边缘:s-maxage(或 CDN 控制台规则)
  • 源站:真实数据

2. 缓存策略配置

2.1 源站响应头

# hash 静态资源(JS/CSS/图片)
Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable

# HTML 入口
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=300

# API 数据
Cache-Control: private, no-store

s-maxage 专门给 CDN / 中间代理:

  • max-age=60, s-maxage=3600:浏览器缓存 1 分钟,CDN 缓存 1 小时

2.2 CDN 控制台规则

优先级:控制台规则 > 源站头。

文件后缀 .js .css .woff2 → 缓存 365 天
文件后缀 .html → 缓存 0 秒(回源)
目录 /api/ → 不缓存

2.3 Vary 头

Vary: Accept-Encoding # gzip/br 分别缓存
Vary: Origin # 不同 CORS origin 分别缓存

Vary: User-Agent 是缓存杀手(几乎不命中),避免使用。

3. 刷新(Purge)

3.1 URL 刷新

精确清除单个 URL 缓存:

# 阿里
aliyun cdn RefreshObjectCaches --ObjectPath "https://cdn.example.com/index.html" --ObjectType File

# AWS
aws cloudfront create-invalidation --distribution-id E123 --paths "/index.html"

# Cloudflare
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
-H "Authorization: Bearer TOKEN" \
-d '{"files":["https://cdn.example.com/index.html"]}'

3.2 目录刷新

# 阿里(慢,配额少)
aliyun cdn RefreshObjectCaches --ObjectPath "https://cdn.example.com/assets/" --ObjectType Directory

# AWS
aws cloudfront create-invalidation --distribution-id E123 --paths "/assets/*"

3.3 全站刷新

# AWS
aws cloudfront create-invalidation --distribution-id E123 --paths "/*"

# Cloudflare
curl -X POST ".../purge_cache" -d '{"purge_everything":true}'

慎用:全站刷新 = 所有节点回源 = 源站瞬时压力大。

3.4 Cache Tag(Cloudflare / Fastly)

给资源打标签,按标签刷新:

Cache-Tag: product-123, homepage
curl -X POST ".../purge_cache" -d '{"tags":["product-123"]}'

最灵活,Fastly 实时生效(< 150ms)。

4. 预热(Prefetch / Prewarm)

刷新后主动让 CDN 节点拉取新资源:

# 阿里
aliyun cdn PushObjectCache --ObjectPath "https://cdn.example.com/app.abc123.js"

# 自定义脚本从各地区节点拉一次
for region in "us" "eu" "cn"; do
curl -H "Host: cdn.example.com" http://${region}-node.cdn.example.com/app.abc123.js > /dev/null
done

大文件 / 大促前预热避免首批用户回源。

5. stale-while-revalidate

Cache-Control: public, max-age=60, stale-while-revalidate=3600

过期后 1 小时内:返回旧缓存(快)+ 后台异步回源更新。

兼顾新鲜度和速度。CDN 和浏览器都支持。

6. stale-if-error

Cache-Control: public, max-age=300, stale-if-error=86400

源站挂了(5xx / 超时),CDN 返回旧缓存而非错误给用户。

生产必加:源站故障时用户仍能看到页面(虽然可能旧)。

7. 前端发版流程

1. 构建新版本 → app.NEW_HASH.js
2. 上传所有新文件到源站 / OSS(不删旧文件)
3. 部署新 index.html(引用新 hash)
4. 刷新 CDN 的 index.html
5. 预热关键资源(可选)
6. 7 天后清理旧版本文件

为什么保留旧文件:

  • CDN 部分节点缓存旧 HTML(引用旧 JS)
  • 用户浏览器缓存旧 HTML
  • Service Worker 缓存旧 HTML

8. 回源率监控

回源率 = 回源请求 / 总请求。健康站点 < 5%。

高回源率排查:
1. Cache-Control 是否正确?
2. URL 是否每次不同(含时间戳/随机参数)?
3. Vary 头是否含 User-Agent?
4. 缓存 key 是否含不必要 query?
5. CDN 控制台规则是否覆盖了源站头?

9. CDN 控制台查看

CDN 状态:
X-Cache: HIT # 命中
X-Cache: MISS # 没命中
Age: 3600 # 已缓存 3600 秒
CF-Cache-Status: HIT # Cloudflare 格式
curl -I https://cdn.example.com/app.js
# 看 X-Cache / CF-Cache-Status / Age

10. 多 CDN 缓存一致性

用两家 CDN 互备时,两家缓存策略要一致:

  • 同一源站
  • 同一 Cache-Control
  • 刷新时两家都刷

11. 常见反模式

  • HTML 配长缓存:发版用户看不到
  • JS 不带 hash + CDN 缓存 1 天:发版 1 天后才生效
  • 每次发版全站刷新:源站压力大 + 缓存命中率归零
  • 不用 stale-if-error:源站挂用户立即 5xx
  • 刷新后不预热:首批用户体验差
  • 发版立即删旧 JS:还在缓存旧 HTML 的用户白屏
  • 不监控回源率:CDN 形同虚设
  • URL 含随机 query:缓存永远不命中
  • Vary: User-Agent:等于不缓存

12. 延伸阅读