缓存基础

CDN 缓存到底要解决什么问题?核心不是“把页面存起来”这么简单,而是把“离用户最近的节点”变成一个稳定的内容分发层。对大多数网站来说,真正的瓶颈不是服务器算力,而是网络往返、资源加载和缓存命中率。把静态资源按类型分层缓存,往往能比升级机器更快地改善体验。

先别把缓存当成“越久越好”

缓存策略最容易出错的地方,是把所有内容都设成长期缓存。HTML、用户个人页、促销页和接口返回值,都不适合用同一套规则。一个典型的例子是电商站:商品详情页每次价格或库存变动都可能需要及时更新,而首页的主图和样式文件可以缓存很久。

Cache-Control 响应头

# 强缓存:浏览器可直接复用,减少回源
location ~* \.(jpg|png|webp)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

# 弱缓存:浏览器仍会验证资源是否变化
location ~* \.html$ {
    add_header Cache-Control "public, max-age=0, must-revalidate";
}

# 不缓存:敏感页面和管理后台始终回源
location /admin/ {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
}

如果你使用 Cloudflare、Fastly 或 Akamai 这类边缘网络,缓存规则通常不只看响应头,还会看 URL、请求方法、Cookie 和查询参数。一个看起来“无害”的 ?v=1.2 参数,可能会让 CDN 误判成新的资源,从而降低命中率。

按文件类型的推荐缓存策略

文件类型 CDN 缓存 TTL 浏览器缓存 TTL 策略说明
HTML 0-1h 0-10min 经常更新,短缓存
CSS/JS(哈希文件名) 365d 365d 文件名变化即新版本
CSS/JS(普通文件名) 7d 1d 适中缓存,版本变化时清除
图片(JPG/PNG/WebP) 30-365d 30-365d 很少变化,长缓存
字体文件 365d 365d 几乎不变
API 响应(GET) 0-5min 0 动态数据,短缓存或不缓存
PDF/下载文件 30d 30d 适中缓存

一个更稳妥的思路

  • 纯静态文件:尽量长期缓存;
  • 页面和接口:使用短 TTL 或按版本控制;
  • 个人化内容:不要缓存,或者只对匿名请求做少量缓存。

缓存清除策略

缓存清理经常比配置更重要。部署新版本、替换图片、修复错误文案,都会触发“旧内容还在边缘节点上被连续命中的”问题。

什么时候需要清除缓存

  1. 部署新版本 — 更新 CSS/JS/HTML 后;
  2. 修复错误内容 — 更正页面中的错误信息;
  3. 更换图片 — 替换产品图片或设计素材;
  4. 紧急更新 — 发布价格变动、公告或安全补丁。

清除方法

# Cloudflare 清除缓存(API)
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://example.com/css/style.css","https://example.com/page"]}'

# 清除全部缓存
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything":true}'

缓存命中率优化

想提升命中率,最有效的并不是“堆更多规则”,而是让资源的 URL 设计更稳定。比较容易忽略的点是:同一资源通过多个 URL 被访问,或者请求里面带了大量跟踪参数。

# 忽略查询参数中的 utm_* 和 fbclid,减少缓存碎片
if ($args ~* "(utm_|fbclid|gclid)") {
    set $args '';
}

推荐做法

  1. 保持一致的 URL — 避免 www 与非 www、带尾斜杠与不带尾斜杠造成缓存分裂;
  2. 设置合理的 TTL — 过短的 TTL 会让缓存失去意义,至少要有 1 小时以上的基础缓存;
  3. 按文件名版本化style.a1b2c3.jsstyle.js 更适合长期缓存;
  4. 静态资源不附带 Cookie — 这样才能真正命中边缘缓存;
  5. 把动态内容与静态内容分离 — 例如图片和 CSS 放 CDN,接口则由应用层控制。

常见场景配置

场景一:WordPress 网站

缓存策略:
- /wp-content/uploads/*       → 缓存 30 天
- /wp-includes/*.css,*.js     → 缓存 365 天
- /wp-admin/*                 → 不缓存
- /*.php                       → 不缓存
- HTML 页面                   → 缓存 1 小时

场景二:静态站点(Astro/Hugo/Next.js 导出)

缓存策略:
- /*.html                      → 缓存 1 小时
- /_next/static/*             → 缓存 365 天
- /assets/*                   → 缓存 365 天
- /images/*                   → 缓存 30 天
- /fonts/*                    → 缓存 365 天

场景三:API 服务

缓存策略:
- GET /api/public/*           → 缓存 5 分钟
- GET /api/prices             → 缓存 1 小时
- POST/PUT/DELETE/*           → 不缓存
- GET /api/user/*             → 不缓存(个性化数据)

四、常见误区与排查清单

缓存策略最容易踩的坑,不是设置得太短,而是把不同内容混在同一套规则里。比如首页的品牌图标、商品页的主图、API 返回值和登录状态页面,本来就不应该使用同一条缓存规则。一个常见的现象是,网站更新了样式文件,但用户仍然看到旧页面,这通常意味着 CDN 还保留了旧版本缓存,或者边缘节点没有正确收到新的响应头。

排查时可以按下面顺序做:先看响应头是否含有正确的 Cache-Control,再确认 URL 是否稳定,最后检查是否有带有 Cookie 的个性化请求把静态资源误判成动态内容。对大多数站点来说,把静态资源和动态内容分层处理,会比盲目增加 TTL 更有效。

参考:Cloudflare Cache Rules 文档 https://developers.cloudflare.com/cache/;MDN Cache-Control 说明 https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control

如果你正在做网站性能优化,缓存策略往往不是“能不能加”,而是“哪些内容适合缓存,哪些内容需要实时性”。这件事做对,往往比换服务器更划算。