我开始的想法
我的博客是 Nuxt 生成的静态站,部署在 Cloudflare Pages。为了让国内访问好一点,域名又套了一层 Cloudflare 优选 CNAME。
最后的链路大概是这样:
访客 -> blog.zery.cn -> 优选 CNAME -> Cloudflare Pages -> 静态文件
然后我发现个问题:
优选只是在选好线路,该加载的资源不会变。
最终访问 blog.zery.cn 时,除了路线,网站本应加载的资源对速度也有一定影响,特别是大头文件,正确规划他们的缓存规则就很重要了, 边缘缓存、浏览器缓存...
我一开始想偷懒:全站直接 /* 缓一年,多省事。后来发现 Nuxt 静态站大部分博客都是不能这么粗暴,尤其是 _nuxt 下面那些带 hash 的资源和普通 HTML 不是一类东西。
缓存
当时的心理活动:都静态博客了,给我缓存一年怎么了?
不过话说回来,缓存对速度的帮助确实很明显,不是那种“心理优化”。最直观的例子就是刷新当前页面:有缓存时,浏览器能直接用本地缓存,或者很快从离自己近的 CDN 节点拿到结果,体感就是几乎秒开;没有缓存时,就要重新走一遍请求链路,HTML、payload、CSS、JS 都得重新拉,慢的时候差距非常明显。
三类缓存文件
Nuxt 静态博客生成出来后,常见资源大概分三类。
第一类是页面和数据,比如首页、文章页、归档页、_payload.json。这些内容会跟着发文章变化,适合短缓存。
第二类是 _nuxt 里的 JS/CSS。它们通常文件名带 hash,内容变了文件名也会变。这类资源适合长缓存,Cloudflare Pages 默认就会给比较激进的缓存头1年。
第三类是预览页、后台接口一类的路径。如果内容可能随时变,或者不想被中间层存住,就应该加 no-store。
HTML / payload 短缓存 /_nuxt/* 让 Nuxt / Pages 默认处理 /preview* 不缓存 字体、图片转换结果 长一点缓存
最好不要直接写全站 /*
我踩过的坑是:Cloudflare Pages 的 _headers 不是“最后命中一条就结束”。多个规则同时匹配时,响应头可能会合并。
如果你写:
/* Cache-Control: public, max-age=300, s-maxage=1800 /_nuxt/* Cache-Control: public, max-age=31536000, immutable
访问 /_nuxt/entry.xxx.css 时,它既命中 /*,也命中 /_nuxt/*。结果就可能变成两个 Cache-Control 混在一起:
Cache-Control: public, max-age=300, s-maxage=1800, public, max-age=31536000, immutable
这就很尴尬了。你本来想让 _nuxt 走一年强缓存,结果短缓存也混进来了。到底哪个生效、不同中间层怎么理解,都不如一开始就别让它混在一起。
我最后用的规则
我的文章路径是 /2026/hello-world 这种年份开头,所以用 /20* 来覆盖文章页和文章 payload。它不会碰到 /_nuxt/*,维护起来也比每篇文章手写路径舒服。
/ ! Cache-Control Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /:page ! Cache-Control Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /:page/ ! Cache-Control Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /:page/_payload.json ! Cache-Control Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /20* ! Cache-Control Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /api/* ! Cache-Control Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /fonts/* ! Cache-Control Cache-Control: public, max-age=31536000, immutable /_ipx/* ! Cache-Control Cache-Control: public, max-age=2592000, stale-while-revalidate=86400 /assets/* ! Cache-Control Cache-Control: public, max-age=604800, stale-while-revalidate=86400 /preview ! Cache-Control Cache-Control: no-store /preview/ ! Cache-Control Cache-Control: no-store /preview/* ! Cache-Control Cache-Control: no-store
这里的 ! Cache-Control 是为了先移除已有的同名响应头,再写自己的值。这样可以减少重复 header 的合并。
缓存头的每个值
短缓存这一行:
Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400
我的理解是:
max-age=300:浏览器本地缓存 5 分钟,文章改了不至于在读者电脑里卡太久。s-maxage=1800:共享缓存,比如 CDN,缓存 30 分钟。stale-while-revalidate=86400:缓存过期后,可以先返回旧内容,同时后台去更新。
这套配置比较适合静态博客:平时访问能吃到 CDN 缓存,发文章后如果很急,可以手动清一下缓存;不急的话,等一会儿也会慢慢刷新。
长缓存这一行:
Cache-Control: public, max-age=31536000, immutable
只适合文件名会变的资源,比如字体、带 hash 的静态资源。别给 HTML 这么写,不然你改了文章,读者浏览器可能还在看很久以前的页面。
自己测一下
不要只看后台规则有没有保存。最终以线上域名返回的响应头为准。
PS>curl.exe -I https://blog.zery.cn/PS>curl.exe -I https://blog.zery.cn/2026/hello-worldPS>curl.exe -I https://blog.zery.cn/archive/_payload.jsonPS>curl.exe -I https://blog.zery.cn/_nuxt/entry.CgjSPprJ.cssPS>curl.exe -I https://blog.zery.cn/preview正常情况下,我希望看到类似这样:
/ public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /2026/hello-world public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /archive/_payload.json public, max-age=300, s-maxage=1800, stale-while-revalidate=86400 /_nuxt/entry.xxx.css public, max-age=31536000, immutable /preview no-store
如果 _nuxt 的 CSS / JS 也变成了 max-age=300,基本就是规则写宽了。回去检查有没有 /*、/:year/:slug 这种把 _nuxt 一起扫进去的规则。
CF-Cache-Status 和 Age 可以顺手看,但别太相信第一次结果。刚部署完、刚清缓存、或者某个边缘节点第一次访问,MISS、Age: 0 都很正常。多请求几次,或者换一个页面再看,才比较接近真实状态。
在哪里配置
如果是 Cloudflare Pages 静态站,我更愿意把这些规则放在仓库的 public/_headers。
原因很简单:它跟着代码走。换机器、重新部署,都不会忘记自己当时到底在后台点了什么。
我也试过直接在 Cloudflare 控制台的 Cache Rules 里配,但这条链路下不稳定,至少我这里没生效。原因是 blog.zery.cn 前面走了优选 CNAME,DNS 记录本身不是普通小黄云代理入口;Cache Rules 要在“这次请求真正经过的那个 Cloudflare 代理层”执行,才会改到最终响应。
所以我最后的判断标准很简单:后台缓存规则保存成功了不算,curl -I https://blog.zery.cn/... 看到 Cache-Control 真的变了才算数。既然我控制台规则试下来没改动响应头,就把缓存规则写到 Pages 输出的 _headers 里,让cf能get到我的缓存规则。
当然,如果你前面还有一层别的 CDN 或优选服务,它也可能有自己的缓存规则。我的习惯是先让看看源站响应头怎么缓存,看前面的 CDN 对它有没有影响。
更新文章后怎么办
这套规则不是“永远不刷新”,而是把刷新节奏压到一个能接受的范围。
平时发文章,我一般这样处理:
- 提交文章,等 Cloudflare Pages 重新部署。
- 线上用
curl -I看首页、文章页、payload 的Cache-Control。 - 如果只是普通更新,等短缓存自然过期。
- 如果是错别字、链接错误、重要内容,直接去 CDN 后台清缓存。
优选域名只是让访问路线更顺一点,缓存规则才是让第二次、第三次访问轻一点。两者不是互相替代的关系,各行其责,让网站访问体验更好。

评论区
评论加载中...