Cloudflare Cache Rules 实战:把 WordPress 首页 TTFB 压到 100ms 以内

WordPress 默认对 HTML 页面发的是 Cache-Control: no-cache, must-revalidate。Cloudflare 老实照做,于是HTML 从来没进过缓存——CSS、JS、图片都缓了,偏偏最贵的那次请求(动态拼装首页)每次都在回源。

结果就是:CDN 明明开着,首页 TTFB 还是三四百毫秒,流量一大 PHP-FPM 就排队。

这篇做的只有一件事:让「未登录访客看到的成品页面」缓存在边缘,命中后不再回源。

问题出在哪:HTML 没进过缓存

curl -sI https://你的站/ | grep -i -E 'cf-cache-status|cache-control|age'

看到 cf-cache-status: DYNAMIC、cache-control: no-cache —— 就是它。Cloudflare 认为 HTML 是动态的,不缓存。

真正要缓存的是渲染完成的 HTML:对访客而言,首页在一段时间内是同一份内容,完全可以复用。

请求链路长这样

Cache Rules 后的缓存链路

访客 → Cloudflare 边缘 → 命中缓存就直接返回(不回源)→ 未命中才回源 WordPress 一次,并把结果写进边缘缓存供后来者复用。

操作步骤速览

Cache Rules 配置步骤

一、先看现状:确认 HTML 确实没被缓存

步骤 1:确认 HTML 未被缓存

$ curl -sI https://pddpdd.cn/ | grep -i -E 'cf-cache-status|cache-control'
cf-cache-status: DYNAMIC
cache-control: no-cache, must-revalidate, max-age=0

DYNAMIC 的含义是:这条请求压根没参与缓存判定。确认了病根,才有改的必要。

二、建 Cache Rule:只缓存"该缓存的 HTML"

步骤 2:创建缓存规则

Cloudflare 面板 → Rules → Cache Rules → Create rule,配置三项:

  • 名称:Cache HTML (guests)
  • 匹配(When incoming requests match):
    http.host eq "pddpdd.cn"
    and not starts_with(http.request.uri.path, "/wp-")
    and not http.cookie contains "wordpress_logged_in"
  • 动作(Then):
  • Cache eligibility → Eligible for cache
  • Edge TTL → Override origin,2 hours
  • Browser TTL → Override origin,no-store

为什么 Edge TTL 设 2 小时:教程站发文频率不高,2 小时能在"命中率"与"新鲜度"之间取平衡;发文后还有主动 purge 兜底(见第四节)。

为什么 Browser TTL 用 no-store:让浏览器每次都回头问边缘,这样 purge 才能立刻生效。别让访客本地缓存卡住更新。

三、给后台和登录态留后门

步骤 3:为登录态和后台放行

缓存规则最容易出的事故,是登录用户被当成访客缓存——A 登录后看到的"我的后台",B 打开同一 URL 也看到了 A 的内容。必须用 Bypass 规则排除:

规则 匹配条件 动作
Bypass logged-in http.cookie contains "wordpress_logged_in" 或 http.cookie contains "wp-postpass" Bypass cache
Bypass admin starts_with(http.request.uri.path, "/wp-admin") 或 "/wp-json" 或 "/xmlrpc.php" Bypass cache

⚠️ 顺序很关键:Cache Rules 按从上到下的顺序匹配,命中即停。所以Bypass 规则必须排在缓存规则前面,否则先命中缓存规则,后门就失效了。

四、验证与清缓存

步骤 4:验证命中并清缓存

$ curl -sI https://pddpdd.cn/ | grep -i cf-cache-status
cf-cache-status: MISS          # 第一次:未命中,回源
$ curl -sI https://pddpdd.cn/ | grep -i cf-cache-status
cf-cache-status: HIT           # 第二次:命中,边缘直接返回
$ curl -sI https://pddpdd.cn/wp-admin/ | grep -i cf-cache-status
cf-cache-status: BYPASS        # 后台:正确绕过

三个状态都对上,就到位了。

发文后记得 purge:有新文章时,首页和归档页要立刻更新。本系统 run_daily.py 每次发文后会自动清除 CF 边缘缓存,手动的话去 面板 → Caching → Configuration → Purge Everything。

三个常见的坑

  • 无脑缓存全部 HTML:登录态、评论、购物车会串站。一定要靠 cookie 条件把登录用户排除掉。
  • Edge TTL 设太长:设成 1 天又不 purge,访客看到的是昨天的首页。2 小时 + 发文即 purge,是省心组合。
  • 忘了移动端:桌面和移动端首页可能不同。若主题做了 UA 分流,要么让主题输出正确的 Vary,要么按 http.user_agent 拆成两条规则,别共用一份缓存。

小结

WordPress 的性能瓶颈往往不在 PHP,而在"每个访客都在让 PHP 重新拼一次同样的页面"。Cache Rules 把这件事从"每人一次"改成"每 2 小时一次",回源量断崖式下跌,TTFB 自然下来。

配合 Redis 对象缓存(压掉回源后的 SQL)和 R2 图床(压掉图片流量),一套下来就是本站现在的形态——动的是配置,不是代码。

常见问题

缓存 HTML 会不会把登录用户看到的内容串给别人?

会,如果你无脑全站缓存 HTML。正确做法是在 Cache Rule 里加一条 Bypass 条件:请求带 wordpress_logged_in_* Cookie 时跳过缓存,只让未登录访客走缓存。

cf-cache-status 一直显示 DYNAMIC 是什么意思?

表示这条请求根本没进 Cloudflare 的缓存判定。HTML 类型默认就是 DYNAMIC,必须用 Cache Rules 显式声明"可缓存"才会开始缓存。

相关阅读

上一篇 Claw Cloud Run 免费容器:每月 $5 额度,白嫖一个常驻小服务
下一篇 用 Cloudflare R2 搭免费图床:10GB 存储 + 零出口费 + 防盗链