缓存规则:把哪些请求留在边缘
读完这篇你能做到:给自己的站点设计一套缓存策略,让静态资源在边缘长期驻留、页面按需短缓存、接口与后台完全不缓存,并且能用响应头判断某次请求到底命中了没有。
缓存做对了,回源次数和首字节时间会一起下降;做错了,用户看到的是改了没生效的旧页面,排查起来比性能问题更烦。这篇的顺序是先看默认行为,再设计规则,最后验证与清除。
1. 默认行为
Cloudflare 默认只缓存一部分静态扩展名(CSS、JS、图片、字体、音视频等),HTML 页面默认不进缓存。缓存键默认是主机名加路径加查询串。
这个默认值对纯静态站偏保守:每个页面请求都会回源,Pages 的回源很快,但仍有往返。想让 HTML 也进边缘,得自己加规则。
另一个默认值来自源站的响应头。源站给了 Cache-Control: max-age=...,边缘按它算 TTL;源站什么都没给,边缘用启发式缓存,行为不好预测。所以规则之外,源站头也要写清楚。
2. 按路径分四类
| 路径 | 缓存资格 | Edge TTL | Browser TTL |
|---|---|---|---|
/assets/*、/img/* | 缓存 | 1 年 | 1 年 |
/*.html、/ | 缓存 | 10 分钟 | 5 分钟 |
/api/* | 绕过 | 不缓存 | 不缓存 |
/admin/* | 绕过 | 不缓存 | 不缓存 |
静态资源带内容哈希时才能给 1 年。immutable 的意思是"这一年里别来问我",文件名不变就永远拿不到新版本。固定文件名配长 TTL 会在改图后出问题,这一点在 73-image-optimization.md 里讲过。
HTML 用短 Edge TTL 是折中。10 分钟意味着发布后最多 10 分钟旧页面消失,访问量大的站点这段时间里绝大多数请求仍然命中边缘。要求发布即生效的站点,把 HTML 设成绕过缓存,成本是每次都回源。
接口和后台直接绕过。带登录态的响应一旦被缓存并被别人拿到,是安全事故而不是性能问题。
3. 在控制台建规则
位置在域名详情 → Caching → Cache Rules → Create rule。每条规则三部分:匹配表达式、缓存资格、TTL 设置。
静态资源那条:
(http.request.uri.path matches "^/(assets|img)/")
Cache eligibility: Eligible for cache
Edge TTL: Ignore cache-control header and use this TTL — 1 year
Browser TTL: Override origin — 1 year
页面那条用扩展名与根路径匹配:
(http.request.uri.path eq "/" or http.request.uri.path contains ".html")
Edge TTL: 10 minutes
Browser TTL: 5 minutes
接口与后台合起来一条绕过规则,表达式用 startsWith 更省事:
(http.request.uri.path starts_with "/api/" or http.request.uri.path starts_with "/admin/")
Cache eligibility: Bypass cache
命中顺序按规则列表从上到下,前面命中就停。把绕过规则放在最上面,可以避免后续规则误伤。
4. 源站头与边缘规则的分工
两层都能设 TTL,容易打架。约定一条界线会清爽很多:
源站负责语义。HTML 页面给 Cache-Control: public, max-age=0, must-revalidate,表达"内容会变,用之前确认一下"。带哈希的静态资源给 max-age=31536000, immutable。
边缘负责兜底与放大。源站头不完整时,用 Cache Rules 覆盖。
Pages 项目里源站头写在 _headers 文件,随产物一起上传。免费档限制是 100 条规则、单条 2,000 字符,够常规站点用。
/assets/*
Cache-Control: public, max-age=31536000, immutable
/*
X-Content-Type-Options: nosniff
5. 验证命中
改完规则要确认生效,不能只看控制台的规则列表。用 curl 看响应头:
curl -sI https://example.com/assets/app.a1b2c3.js | grep -i -E 'cf-cache-status|age|cache-control'
cf-cache-status 的几个取值含义:
| 值 | 含义 |
|---|---|
HIT | 命中边缘缓存 |
MISS | 未命中,本次回源并写入缓存 |
DYNAMIC | 按规则不缓存 |
BYPASS | 规则或请求头要求绕过 |
EXPIRED | 有缓存但已过期,重新回源 |
同一个 URL 连请求两次,第一次 MISS 第二次 HIT,说明规则在起作用。两次都是 MISS,检查是否带了 Set-Cookie、Cache-Control: no-store,或者请求方法不是 GET。
age 头显示这份缓存已经存在多少秒,用来判断 TTL 是否符合预期。
6. 改完不生效怎么办
先确认规则顺序。后面的规则不会覆盖前面已经命中的。
再清除缓存。单 URL 清除在 Caching → Configuration → Purge,按 URL 精确清除,秒级生效。整站清除影响面大,非必要不用。
Dev Mode 可以临时让所有请求绕过缓存,持续三小时后自动关闭。调试期间打开它,避免一边改一边被旧缓存干扰。
如果清除了还是不生效,检查浏览器缓存。Browser TTL 设成 1 年的资源,在用户端也留了一年,边缘清了不代表浏览器清了。
常见问题
为什么 HTML 没被缓存。
默认扩展名列表里没有 HTML。要么加一条显式的 Cache Rule,要么在源站头里给 HTML 一个 s-maxage。两者选一个,别同时用。
免费档能建多少条规则。
Cache Rules 在免费档可用,规则条数有上限;旧的 Page Rules 免费档是 3 条。新项目直接用 Cache Rules,表达能力和可读性都更好。
缓存了带 cookie 的响应怎么办。
按路径绕过,或者在规则里加"存在 Set-Cookie 时不缓存"的条件。更稳的做法是让需要登录的路径全部走绕过规则,不给缓存层判断的机会。
小结
缓存规则的骨架是分类:长期不变的静态资源、会变的页面、不能缓存的接口和后台。三类各自一条规则,顺序上绕过在前。
规则只是声明,验证要靠响应头。cf-cache-status 从 MISS 变 HIT 才算真的生效。剩下的事是清理:单 URL 清除处理个别文件,Dev Mode 处理调试期,整站清除尽量少用。
响应头在 Pages 项目里写在 _headers,随构建产物一起上传,产物目录的关系见 67-pages-getting-started.md;重定向与缓存的交互见 75-redirects-bulk.md。
数据来源与检索时间
检索时间 2026-11-10。
| 来源 | URL | 对应数据 |
|---|---|---|
| Cache Rules 文档 | https://developers.cloudflare.com/cache/how-to/cache-rules/ | 规则组成、缓存资格与 TTL 选项、按表达式匹配 |
| 缓存响应头说明 | https://developers.cloudflare.com/cache/concepts/cache-responses/ | cf-cache-status 各取值含义 |
| Pages Limits | https://developers.cloudflare.com/pages/platform/limits/ | _headers 最多 100 条规则、单条 2,000 字符 |
| 清除缓存 | https://developers.cloudflare.com/cache/how-to/purge-cache/ | 单 URL 清除与整站清除、Dev Mode 的用途 |
核验说明
- 文档部分:
_headers的条数与单条长度限制来自 2026-11-10 抓取的 Pages Limits 页面;cf-cache-status取值与规则选项依据 Cache 文档。 - 未执行:本机 Token 没有 Zone 权限,没有创建过任何 Cache Rule,也没有执行过清除缓存或开启 Dev Mode。控制台步骤全部依据文档描述。
- 未执行:
curl -sI的验证命令没有对真实站点运行,示例输出未贴出真实结果。 - 未核实:Cache Rules 在免费档的具体条数上限,正文只说"有上限"没有给数字;Page Rules 免费 3 条为长期公开的额度,未在控制台核对。
- 本地构建验证:无。