Cloudflare 缓存规则(Cache Rules)实战:静态资源长缓存、匿名 HTML 短缓存与缓存键调优全流程

为什么你的站明明套了 CDN,命中率却低得可怜

很多个人站长把域名接上 Cloudflare 之后,就以为静态资源会自动被边缘节点缓存,结果打开 Cloudflare 后台一看缓存命中率只有百分之十几,源站带宽账单一点没降。问题往往不在 CDN 本身,而在于你没告诉它「什么该缓存、缓存多久」。Cloudflare 早期靠 Page Rules(页面规则)做这件事,但 Page Rules 数量少、优先级难排、表达力弱,免费版只有 3 条,很难精细控制。现在官方主推的是 Cache Rules(缓存规则),它和 WAF 规则共用同一套规则引擎,表达式灵活、条数充足,是个人站长真正该用来调缓存的工具。

这篇文章不堆术语,而是按「一个真实站长站点」的视角,从默认缓存行为讲起,一步步用 Cache Rules 把静态资源、图片、HTML 页面分别设成不同的缓存策略,再解决最常见的「缓存了更新却看不到」「缓存了登录态被串」两类事故。全文基于免费计划可用的功能,不需要付费。

先搞清楚 Cloudflare 默认缓存了什么

Cloudflare 的默认缓存行为其实很克制。它对下面这些扩展名的请求默认走缓存:

图片类 jpg jpeg png gif webp svg ico;样式脚本类 css js;字体 woff woff2 ttf otf;以及 mp4 pdf zip 等静态文件。除此之外,HTML 文档默认是「不缓存」的(Browser Cache TTL 那套另说,那是浏览器端)。这就是为什么你首页的 HTML 每次访问都回源——Cloudflare 有意不缓存动态页面,避免把登录信息、个性化内容缓存出去。

还有一个隐藏规则:Cloudflare 只对 GET 请求缓存,并且会看响应头。如果你的源站返回了 Set-Cookie、Cache-Control: private、no-store,边缘节点就拒绝缓存。很多 PHP 程序在页面里调用了 session_start(),PHP 自动吐出 Set-Cookie: PHPSESSID=...,于是整站 HTML 谁都缓存不了。这是命中率低的头号原因。

进 Cache Rules 的入口和基本写法

登录 Cloudflare 后台,选中你的域名,左侧菜单 Caching → Cache Rules → Create rule。一条规则由三部分组成:

第一是表达式(Expression),用规则引擎语法描述「匹配哪些请求」。点击 Edit expression 可以用可视化编辑器,也可以直接写。常见字段有 http.request.uri.path(路径)、http.request.uri.path.extension(扩展名)、http.host(域名)、http.request.method(方法)。

第二是动作(Then)。缓存相关的关键动作有:Cache eligibility(Eligible / Bypass,即是否允许缓存)、Edge TTL(边缘缓存时长,可 Override origin / Use cache-control header / 指定秒数)、Browser TTL(浏览器缓存时长)、Cache Key(缓存键,决定用什么区分不同缓存副本)。

第三是顺序。规则从上到下匹配,第一位命中即生效。所以「例外规则」要放在前面。

实战一:给静态资源设一年缓存

静态资源文件名通常带版本号或哈希(app.3f9a2.js),内容变了文件名就变,天然适合长缓存。新建一条规则:

表达式写:

(http.request.uri.path.extension in {"js" "css" "woff2" "woff" "ttf" "png" "jpg" "jpeg" "gif" "webp" "svg" "ico" "mp4"})

动作设置:Cache eligibility 选 Eligible;Edge TTL 选 Override origin 并填 31536000(一年);Browser TTL 同样 Override 填 31536000。这样无论源站有没有正确配置缓存头,Cloudflare 都会强制把这批文件缓存一年,回源次数断崖式下降。

要注意的前提是:你的静态资源必须带版本指纹。如果你的 CSS 永远叫 style.css,设一年缓存意味着你改完样式,用户一年内看到旧样式,怎么刷新都没用。没做指纹的站,先把时长压到一天(86400),等接入构建工具生成哈希文件名后再放开。

实战二:图片和视频单独走更长策略

图片目录通常是 /uploads/ 或 /wp-content/uploads/。可以对路径前缀做规则,顺便打开 Cloudflare 的图片优化能力(免费计划含 Polish 的有限能力,可用 Mirage 也可配合后续)。表达式:

(starts_with(http.request.uri.path, "/wp-content/uploads/"))

动作:Eligible、Edge TTL 一年。图片一般不会带 Cookie,是干净的缓存对象。如果你的网站在上传目录上做了防盗链或权限校验,请勿套这条规则,否则会把「需要鉴权的图片」缓存给所有人。

实战三:HTML 页面要有条件地缓存

这是最容易出事、也最见功力的部分。给 HTML 做边缘缓存能极大降低源站压力(尤其对 WordPress 这种每次请求都查库的程序),但必须保证「匿名访客看到的页面」和「登录用户看到的页面」是两个缓存键,否则会串号。

安全做法是只缓存匿名 GET 请求的 HTML。表达式:

(http.request.method eq "GET"
 and not http.cookie contains "wordpress_logged_in"
 and not http.cookie contains "PHPSESSID"
 and http.request.uri.path.extension in {"" })

动作:Eligible、Edge TTL 设为 600(十分钟)到 3600 之间。为什么留短一些?因为 HTML 更新频繁,你发一篇新文章希望尽快可见,十分钟的延迟是多数站长能接受的。如果你站点更新很少,可以放到一天。

关键在于「带登录 Cookie 就不缓存」这个条件。not http.cookie contains ... 让所有已登录用户的请求绕过边缘缓存,直接回源看实时内容;匿名访客则共享同一份缓存副本。这样既有缓存收益,又不会把后台界面缓存给访客。

Cache Key:决定「谁和谁共用一份缓存」

Cache Key 是缓存规则里最被低估的一项。默认情况下 Cloudflare 用「完整的 URL」做键,也就是说查询串不同就当不同资源。比如 /list.php?page=1 和 /list.php?page=2 会分别缓存,这没问题;但如果你的站带了 ?utm_source=xxx 这类营销参数,同一篇文章会因为不同 utm 参数缓存几十份副本,命中率被稀释。

解决方法是把这类无关参数剔除出缓存键。在规则动作里找到 Cache Key → Query String,选 Include specified query string parameters,只保留真正影响内容的参数(比如 id、page、p)。或反过来 Ignore 掉 utm_*、fbclid、gclid。WP 站点尤其要加 fbclid,否则 Facebook 分享链接会污染缓存。

另一个常见需求是「忽略来源站做伪静态」的站:如果你的 URL 里带 ?from= 只为统计,把它从缓存键移除能让不同来源的访客共享同一份缓存。

绕过缓存(Bypass)的正确姿势

有些路径永远不该缓存:后台 /wp-admin/、/admin/、带敏感参数的接口、购物车、支付回调。给它们建一条 Bypass 规则,放在所有缓存规则的最前面:

(starts_with(http.request.uri.path, "/wp-admin/")
 or starts_with(http.request.uri.path, "/cart")
 or http.request.uri.path contains "/checkout")

动作选 Bypass cache。虽然 Cloudflare 默认不缓存 HTML,但如果你前面写了「缓存 HTML」的宽泛规则,一定要用 Bypass 把它盖住。规则从前往后匹配,例外写在前,是缓存规则设计的第一原则。

清缓存与「改了看不到」的排查

Cache Rules 生效后,最常见的抱怨是「我更新了文章,前台还是旧的」。按这个顺序排查:

第一步,确认缓存层级。可能是浏览器缓存,不是 Cloudflare。用无痕窗口或 curl -I 看响应头里的 cf-cache-status。HIT 表示命中了边缘缓存;MISS 表示回源了;EXPIRED 表示缓存过期正在回源更新。cf-cache-status: DYNAMIC 则说明该请求根本没进缓存流程。

第二步,若确实是边缘 HIT 住了旧内容,去后台 Caching → Configuration → Purge Cache 清。免费版可以 Purge Everything(清全站),也可以按 URL 精确清(单个 URL 或前缀)。WordPress 站长建议装官方 Cloudflare 插件,它能让你在发布文章时自动 purge 对应 URL,免去手工操作。

第三步,验证 purge 是否生效:curl -sI "https://你的域名/文章路径?_=$(date +%s)" | grep cf-cache-status,加时间戳参数能强制看到源站最新响应。如果还是旧内容,很可能是源站自身或上游还有一层缓存(比如 Nginx proxy_cache),需要一并清。

一条推荐的规则顺序模板

把上面的实践串起来,一个稳妥的规则顺序是:

第一条,Bypass 后台、购物车、支付类路径(例外优先)。第二条,Bypass 一切带登录 Cookie 的请求(保证登录用户实时)。第三条,静态资源(扩展名匹配)Eligible + 一年 TTL + Cache Key 忽略 utm。第四条,上传目录图片 Eligible + 一年 TTL。第五条,匿名 GET 的 HTML Eligible + 十分钟 TTL。所有规则末尾都用 starts_with 或扩展名精确限定,避免宽泛匹配误伤。

写完别急着上线,先用 Cloudflare 后台的 Trace 工具(在域名概览页)或规则的「测试」功能,粘一个真实 URL 看它命中的是哪条规则、最终缓存结论是什么。规则引擎的表达式是「与或非」逻辑,一处括号写错就会让整条规则失效或误伤,测试比猜更省时间。

和源站缓存头的分工

最后强调一个容易混乱的点:Cloudflare 的边缘缓存和源站(Nginx / PHP)发出的 Cache-Control 是两回事。当 Cache Rules 里 Edge TTL 选了「Override origin」,就以 Cloudflare 的设置为准,源站的头只在浏览器到边缘这一段起作用。反之若选「Use cache-control header」,就要保证源站发的是对的头(public, max-age=...),否则 Cloudflare 会尊重源站的 no-cache 而不缓存。

个人站长的最佳分工是:源站只负责把静态资源的 Cache-Control 发对,动态页面交给 Cloudflare 的 Cache Rules 统一管。这样你换服务器、换框架时,缓存策略不用重配,全在 Cloudflare 一处控制,迁移成本最低。

小结

Cloudflare 的免费缓存能力其实很强,命不命中全看规则写得对不对。核心就三件事:用 Cache Rules 表达「什么该缓存、缓存多久」;用 Cache Key 决定「谁和谁共享缓存」,剔除无关参数;用 Bypass 规则护住后台与登录态。把静态资源设成长缓存并配文件名指纹,把匿名 HTML 设成十分钟短缓存,再给后台和登录请求开例外,你的命中率会从十几个百分点稳定爬到七八成,源站压力肉眼可见地下降。规则写完务必用测试工具验证一遍,别让一条写错的表达式把整个站的缓存策略带偏。

Last modification:October 11th, 2026 at 12:26 pm

Leave a Comment