我为什么把 Page Rules 换成了 Cache Rules
三月末给这个站配缓存的时候,Page Rules 面板上多了一行提示:新建规则会走 Cache Rules。我原来那条 pddpdd.cn/wp-content/* 还在跑,没急着动,但顺手点进表达式编辑器看了一眼,就回不去了。
旧方案能写路径通配。新方案能判断 Cookie、请求头、来源国家、设备类型,还能读查询串里有没有某个参数。差的不是功能多少,是有很多逻辑以前要建三条规则互相覆盖才表达得出来,现在一句就够了。
下面是我把 pddpdd.cn 的首页 TTFB 从 460ms 压到 90ms 左右的全部过程。中间把后台和登录态搞坏过两次,也一并写进来。
两套规则到底差在哪

真正的分水岭是匹配能力。Page Rules 只能写 pddpdd.cn/wp-content/* 这种路径通配。Cache Rules 能写完整表达式:
(http.host eq "pddpdd.cn" and not starts_with(http.request.uri.path, "/wp-admin"))
这句话的意思是:只对本站生效,而且把后台路径排除掉。一行说清楚「缓存前台、绕过后台」这件事。
用 Page Rules 表达同样的意思,我当时建了三条:/wp-admin/ 设 Bypass、/wp-content/ 设缓存、其余设缓存。三条规则的先后顺序不能错,后来加了个 /wp-json/* 又要插一条到最前面。这就是我下决心迁移的原因。
一次请求是怎么走的
先把链路摆清楚,不然配规则全靠猜。

访客第一次请求,边缘节点没有副本,回源拿到页面存一份。第二次请求同一个 URL,边缘直接返回,源站完全不参与。所以配对的站点,源站 CPU 曲线会平得像条直线。
有个特别容易搞混的地方:回源次数少,不等于 PHP 执行少。如果页面本身是 DYNAMIC,每次都要跑一遍 WordPress,边缘只是每次都老老实实去要。判断有没有真的命中,只看一个响应头:

cf-cache-status 常见的几个取值:
| 值 | 含义 | 该做什么 |
|---|---|---|
HIT | 命中边缘缓存 | 正常,这就是目标状态 |
MISS | 边缘没有,已回源并缓存 | 首次请求,正常 |
EXPIRED | 缓存过期,回源更新 | TTL 大概率设短了 |
DYNAMIC | 压根没进缓存 | HTML 默认是这个,要配规则 |
BYPASS | 被规则显式跳过 | 查是不是命中了自己的绕过规则 |
STALE | 返回了过期副本 | 源站可能有问题 |
REVALIDATED | 304 确认后返回缓存 | 正常 |
第一次看到首页是 DYNAMIC 的时候我以为是 CF 没生效。其实就是 HTML 默认不进缓存,Cloudflare 一直是这样。
三步配完 Cache Rules
控制台路径:你的域名 → Caching → Cache Rules → Create rule。

第一步:静态资源长缓存
(http.host eq "pddpdd.cn" and starts_with(http.request.uri.path, "/wp-content/"))
or (http.host eq "pddpdd.cn" and starts_with(http.request.uri.path, "/wp-includes/"))
- Cache eligibility:
Eligible for cache - Edge TTL:
Ignore cache-control header and use this TTL→1 year - Browser TTL:同上 →
1 year
图片、CSS、JS 内容一旦生成就不变,而且 WordPress 会带版本号查询串,改版本自然换 URL,长缓存没有副作用。
第二步:HTML 短缓存
WordPress 的 HTML 默认带 Cache-Control: no-cache, must-revalidate,边缘看到这个头就不存。所以要缓存,必须显式覆盖。
(http.host eq "pddpdd.cn"
and not starts_with(http.request.uri.path, "/wp-admin")
and not starts_with(http.request.uri.path, "/wp-login.php")
and not starts_with(http.request.uri.path, "/wp-json")
and not contains(http.request.uri.path, "preview=true")
and not any(http.request.cookies["wordpress_logged_in_*"][*] != ""))
- Cache eligibility:
Eligible for cache - Edge TTL:
Ignore cache-control header and use this TTL→4 hours - Browser TTL:
Ignore cache-control header and use this TTL→30 minutes
最后那行 Cookie 判断是全篇最重要的一句。我自己第一次配的时候漏了它,结果登录着后台去看前台文章,看到的是半小时前的旧版本,评论区状态也是乱的,一度以为是主题的 bug。
💡 站上有评论、购物车、会员这类插件的话,Edge TTL 别超过 1 小时,Cookie 判断那行必须保留。
第三步:动态路径显式绕过
(starts_with(http.request.uri.path, "/wp-admin")
or starts_with(http.request.uri.path, "/wp-login.php")
or starts_with(http.request.uri.path, "/wp-json")
or eq(http.request.uri.path, "/xmlrpc.php")
or starts_with(http.request.uri.path, "/cart"))
- Cache eligibility:
Bypass cache
Bypass cache 和不建规则不是一回事。Bypass 是显式声明,不会因为你后面加了子级规则、或者手滑调了顺序就被意外命中。我当时把 Bypass 规则放在缓存规则后面,结果 /wp-admin 页面被缓存了,登录直接卡在跳转循环里。
缓存键这两个地方得改

把无意义的查询参数忽略掉
WordPress 会生成 ?ver=6.7.1 这类版本串。不去管它的话,插件一升级,?ver= 一变,缓存键就全换了,等于整站缓存一次性作废。做法是打开 Query String Sort,再加一条忽略名单:
utm_source, utm_medium, utm_campaign, fbclid, gclid, ver
别把 Cookie 塞进缓存键
网上有些教程会让你把所有 Cookie 都纳入缓存键。这个方向是反的。每个访客的 Cookie 都不一样,缓存键永远不重复,命中率会掉到接近零,配了等于没配。Cookie 应该只在规则条件里做判断,不要放进键里。
怎么确认真的配好了
别凭感觉,跑这三条。

第一条查首页,第一次 MISS 或 EXPIRED,刷新第二次应该变 HIT。第二条查图片,应该直接 HIT。第三条查后台,/wp-admin 返回 BYPASS 或 DYNAMIC 都对,绝不该是 HIT。
如果首页刷两三次还是 MISS,按这个顺序排查:
- 表达式里
http.host的域名写错了。这一条占了我踩坑次数的一半,pddpdd.cn/末尾多一个斜杠就匹配不上 - 请求带了
Cache-Control: no-cache,而你没勾选忽略源站缓存头 - 有插件在输出
Set-Cookie,Cloudflare 见到 Cookie 就跳过缓存 - 规则顺序错了,Bypass 排在缓存后面
我实际踩过的五个坑
搜索结果页被缓存了
/?s=关键词 命中缓存之后,A 用户搜到的结果会被 B 用户看到。分页 /page/2/ 同理。在 Bypass 规则里补一句:
(contains(http.request.uri.query, "s=") or contains(http.request.uri.path, "/page/"))
更新文章后前台还是旧的
这是缓存生效的表现,等 Edge TTL 到期就好(4 小时)。急着看的话,登出状态下刷新即可,因为登录态本来就不吃缓存。要立刻全站清就在 Caching → Configuration 里 Purge,但别养成习惯,全站清一次所有访客都要重新回源,小机器扛不住。单篇用 Purge by URL 更稳。
Development Mode 忘了关
这个开关会跳过全部缓存,三小时后自动失效。我在上面浪费过一个晚上,一直在查为什么全部 MISS。
没开 Serve Stale
Cache Rules 的 Edge TTL 下面有个 Serve stale content while revalidating。打开之后,缓存过期时访客先拿到旧副本,回源在后台做。页面不用等源站响应,体感差别挺明显。我一开始没开,TTFB 在 90ms 和 300ms 之间来回跳,开了之后就稳住了。
以为它能替代对象缓存
Cache Rules 管的是边缘那一层。源站的 PHP 执行、数据库查询它一概管不到。我这台是 2 核 4G,装了 Redis Object Cache 之后后台的操作响应才顺起来。两件事互补,不是替代关系。
要不要买 APO
Cloudflare 有个付费功能 APO(Automatic Platform Optimization),每月 $5,专门针对 WordPress 优化 HTML 缓存和第三方脚本加载。
我的判断标准:
- 纯博客:Cache Rules 配好就够,钱可以省
- 带会员、评论、电商,又不想自己维护规则:APO 值这个价,它会自动处理 Cookie 分层
- 已经按本文配完并且测速正常:没必要重复付费
先按上面的配一遍,拿 PageSpeed Insights 和 curl 测。首页 TTFB 进 100ms 以内之后,APO 能带来的边际收益已经很小了。我这个站目前的数字是 90ms 上下,就没买。
缓存规则配完之后
这类配置属于「配一次管很久」。我这套规则从三月跑到现在,中间只改过两次,都是因为换了插件。
建议第一次配完就记一组基线:用 PageSpeed Insights 跑一遍首页,把 TTFB、LCP、总请求数抄下来。等哪天换了主题或者加了一堆插件,回头一对比,就知道是哪一步把性能吃掉的。
另外每次 WordPress 升级大版本之后,跑一遍上面那三条验证命令。我遇到过插件升级改写了响应头,把 cf-cache-status 从 HIT 打回 DYNAMIC 的情况。
如果你还在纠结 VPS 那边的优化,可以接着看 VPS 探针怎么选,那篇讲的是怎么在机器出问题之前先知道。用免费容器搭监控面板的话,claw 免费容器完全指南 里有从注册到保活的完整流程。