浏览器缓存和 Cloudflare 缓存不是一回事:Cache-Control、Edge TTL 与发布刷新

从浏览器缓存和 Cloudflare 边缘缓存的区别出发,解释 Cache-Control、max-age、s-maxage、Edge TTL、Browser TTL、purge all 之间的关系,以及静态博客该如何配置缓存。

配置 Cloudflare 缓存时,最容易混淆的是两件事:

  • Cloudflare 边缘节点缓存多久。
  • 用户浏览器本地缓存多久。

这两层都和缓存有关,但控制对象不同,风险也不同。

对静态博客来说,一个比较稳的思路是:Cloudflare 边缘缓存可以激进一点,浏览器缓存要保守一点。因为 Cloudflare 的缓存可以通过后台或 API 清掉,而用户浏览器本地缓存无法由站长直接清掉。

先分清三层位置

一次访问大致经过三层:

1
2
3
4
5
浏览器
Cloudflare 边缘节点
VPS / Nginx / Hugo 静态文件

对应的缓存也有两类重点:

  • Browser cache:浏览器存在用户本机的缓存。
  • Edge cache:Cloudflare 存在边缘节点上的缓存。

源站 Nginx 返回的 Cache-Control 会影响浏览器,也可能影响 CDN。Cloudflare 的 Cache Rules 又可以单独设置 Edge TTL 和 Browser TTL。再加上 Response Header Transform Rules 还能改最终返回给浏览器的响应头,所以如果不分层,很容易以为“设置了缓存”就解决了一切。

Cache-Control 控制的是谁

Cloudflare 学习中心对 Cache-Control 的解释很直接:它是一个 HTTP 响应头,用来告诉浏览器和中间缓存如何处理资源。

常见指令有几个:

1
Cache-Control: public, max-age=300

含义是:这个响应可以被缓存,浏览器最多缓存 300 秒。

1
Cache-Control: private

含义是:只适合浏览器这类私有缓存保存,不应该被 CDN 这类共享缓存保存。

1
Cache-Control: no-store

含义是:不要缓存,适合非常敏感的数据。

1
Cache-Control: no-cache

这个名字容易误导。它不是完全不缓存,而是使用缓存前必须先向服务器确认资源有没有更新。通常会配合 ETagLast-Modified

1
Cache-Control: s-maxage=86400

s-maxage 面向共享缓存,例如 CDN。它可以让 CDN 缓存更久,而浏览器仍按 max-age 或其他规则处理。

所以,Cache-Control 不是只给浏览器看的,也不是只给 CDN 看的。它是一组缓存规则,具体谁采用、谁覆盖,要看中间层怎么配置。

Cloudflare Edge TTL 和 Browser TTL 的区别

Cloudflare 里的两个 TTL 要分开看。

Edge TTL 决定 Cloudflare 边缘节点缓存多久。

例如:

1
Cloudflare Edge TTL = 1 day

意思是 Cloudflare 可以把页面、图片、CSS、JS 等内容存在边缘节点 1 天。访问者再次请求同一个 URL 时,如果缓存命中,Cloudflare 直接返回,不回源到 VPS。

Browser TTL 决定浏览器缓存多久。

例如:

1
Browser TTL = 5 minutes

意思是浏览器收到资源后,本地最多把它当作新鲜资源保存 5 分钟。5 分钟内用户再次访问,浏览器可能直接使用本地缓存;超过 5 分钟后,需要重新验证或重新请求。

两者最大的差别是:

  • Edge cache 可以由站长 purge
  • Browser cache 在用户电脑里,站长没法直接清。

因此,HTML 页面不建议给浏览器很长 TTL。否则文章更新后,即使 Cloudflare 已经清缓存,用户浏览器仍可能继续用本地旧页面。

静态博客为什么适合缓存 HTML

动态网站通常不敢随便缓存 HTML,因为页面可能包含登录状态、用户资料、购物车、推荐内容、权限信息。

静态博客不一样。Hugo 生成的页面通常是纯静态文件:

  • 首页。
  • 文章页。
  • 分类页。
  • 标签页。
  • 多语言页面。
  • RSS。
  • sitemap。

这些内容对所有访客基本一样,所以可以让 Cloudflare 缓存 HTML。这样做能减少回源请求,也能让海外访问更快。

但这里的关键是:

1
2
Cloudflare Edge 可以长一点
浏览器 Browser 要短一点

例如:

1
2
Cloudflare Edge TTL = 1 day
Browser TTL = 5 minutes

这表示 Cloudflare 可以缓存 1 天,但用户浏览器只缓存 5 分钟。

每次发布都 purge all 时,Edge TTL 可以更长

如果发布脚本每次都会调用 Cloudflare purge everything,那么 Edge TTL 可以设得更长。

逻辑是:

1
2
3
平时:Cloudflare 缓存 1 天,减少回源
发布:上传新 public 后 purge all
发布后:Cloudflare 重新回源拿新内容

这种模式适合静态博客。因为站点内容不是实时变化,更新动作也集中在部署时。

Edge TTL 设 1 天和 1 个月都可以,区别主要是风险偏好:

  • 1 day:如果某次 purge 失败,旧内容最多自然保留 1 天。
  • 1 month:缓存命中率更高,但 purge 失败时旧内容可能留更久。

普通个人博客先用 1 day 更稳。

为什么不建议浏览器也长缓存

浏览器缓存越长,重复访问越快,但更新越难控制。

比如 HTML 设置:

1
Cache-Control: public, max-age=31536000

用户访问一次文章页后,浏览器可能把页面当作一年内都新鲜。你后面修改错字、更新链接、修正内容,Cloudflare purge 了也不一定影响这个用户的本地缓存。

静态资源可以长缓存,是因为它们通常带指纹文件名:

1
2
/scss/style.min.5e5f1f8f7ce389becd0a48fe3eff3e1c79b2750a903584200a3a6fc54ae1e664.css
/ts/main.9acdcb7a78e911f2c617b8cd4af2eab573c04169dddc51c052977dde63210ced.js

内容变了,文件名也变,旧缓存不会影响新页面引用的新文件。

但 HTML URL 通常不变:

1
/2026/06/13/browser-cache-vs-cloudflare-edge-cache/

所以 HTML 更适合短浏览器 TTL。

一种简单配置

如果希望简单,不想按文件类型细分,可以用一套统一策略:

1
2
3
Cloudflare Edge TTL = 1 day
Browser TTL = 5 minutes
每次发布 purge everything

Cloudflare Cache Rule 负责边缘缓存:

1
(http.host in {"knightli.com" "www.knightli.com"} and not starts_with(http.request.uri.path, "/cdn-cgi/"))

动作:

1
2
3
4
Eligible for cache
Edge TTL: 1 day
Browser TTL: 5 minutes
Query string: ignore

如果源站 Nginx 对 CSS/JS/图片设置了更长的 Cache-Control,而你又希望所有返回给浏览器的资源都统一为 5 分钟,可以在 Cloudflare 再加一条 Response Header Transform Rule:

1
2
3
4
5
Set:
Cache-Control: public, max-age=300

Remove:
Expires

这样就能做到:

  • Edge 缓存由 Cloudflare Cache Rule 控制。
  • Browser 缓存由 Cloudflare 响应头规则统一控制。
  • Nginx 不需要为了浏览器 TTL 做统一改动。

什么时候需要分层缓存

如果追求更高性能,可以做分层:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HTML:
  Edge TTL: 1 day
  Browser TTL: 5 minutes

CSS / JS / 图片 / 字体:
  Edge TTL: 1 month
  Browser TTL: 1 month 或 1 year

RSS / sitemap:
  Edge TTL: 1 hour
  Browser TTL: 5 minutes

这种配置更细,但维护成本也更高。个人博客一开始可以不用这么复杂。

当你确认所有静态资源都带 hash 文件名,再把 CSS/JS/图片的 Browser TTL 拉长,会更稳。

如何验证是否生效

可以用 curl -I 连续请求同一个 URL:

1
2
curl.exe -I https://knightli.com/
curl.exe -I https://knightli.com/

重点看两个头:

1
2
cf-cache-status: HIT
cache-control: public, max-age=300

cf-cache-status: HIT 说明 Cloudflare 边缘缓存命中。

cache-control: public, max-age=300 说明浏览器端最多缓存 300 秒。

如果看到:

1
cf-cache-status: MISS

可能是第一次访问、刚 purge 过、规则还没生效,或者这个资源不符合缓存条件。再请求一次通常能看到 HIT

如果 CSS/JS 仍显示:

1
cache-control: public, max-age=31536000, immutable

说明浏览器缓存头仍由源站或某条更高优先级规则控制。要么接受静态资源长缓存,要么用 Cloudflare Response Header Transform Rule 覆盖。

小结

浏览器缓存和 Cloudflare 缓存不是一回事。

最实用的判断是:

  • Cloudflare Edge 缓存可以激进,因为站长能 purge。
  • 浏览器缓存要谨慎,因为站长不能清用户本地缓存。
  • HTML 页面适合短 Browser TTL。
  • 带 hash 的静态资源可以长 Browser TTL。
  • 静态博客如果每次发布都 purge all,Edge TTL 设 1 天是一个稳妥起点。

对 Hugo 静态博客来说,先用:

1
2
3
Cloudflare Edge TTL = 1 day
Browser TTL = 5 minutes
每次发布 purge everything

就已经能获得大部分 CDN 加速收益,同时避免文章更新后长期显示旧页面。

参考链接

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计