事情是这样的:最近检查站点 mlsha.cn(一个 WordPress 个人博客)的访问速度,发现响应头里赫然写着 EO-Cache-Status: MISS——也就是说,EdgeOne 边缘节点根本没把 HTML 缓存住,每次请求都回源站。
这其实是 WordPress 站上 CDN 最常见的性能隐患。折腾了一圈,我前后改了 4 版 JSON,踩了 5 个语法坑,才把"缓存规则"和"响应头规则"两套配置都跑通。下面把踩坑过程完整记下来,省得你再掉进去。
一、先划红线:HTML 缓存必须配在 CDN 层
很多人的第一反应是去改源站 Nginx,给 location / 加个 expires。千万别这样做,原因如下:
WordPress 的页面是按用户变化的(登录态、wordpress_logged_in cookie、评论、nonce)。如果你在源站把 PHP 整页当静态缓存,后果是把 A 用户的渲染页发给所有访客:后台页泄露、内容不更新、评论错乱。这是典型生产事故。
正确的链路是:用户 → EdgeOne 边缘(这里决定缓存命中)→ 源站 Nginx(只提供源数据)。
HTML 的边缘缓存,得在 EdgeOne 控制台配;源站 Nginx 只负责把静态资源缓存调长。
二、坑 1:你看到的 API 文档 ≠ 控制台能导入的模板
我第一版照着腾讯云 CreateL7AccRules API 文档写,带了 Status、MaxAge、CacheKey、CachePrefresh 这些字段。结果控制台报"语法未通过"。
根因:"导入规则"这个入口只认一种动作 Cache,参数只有 CacheParameters 三选一:
CustomTime:自定义缓存时长(秒)NoCache:不缓存FollowOrigin:跟随源站
那些花哨字段是 API 层面的,导入模板根本不认。控制台认的骨架长这样(记住它,后面全靠它):
{
"FormatVersion": "1.0",
"Rules": [
{
"RuleName": "WordPress 建站-mlsha.cn",
"Branches": [
{
"Condition": "${http.request.host} in ['mlsha.cn', 'www.mlsha.cn']",
"Actions": [],
"SubRules": [
{
"Branches": [
{
"Condition": "匹配条件",
"Actions": [
{ "Name": "Cache", "CacheParameters": { ... } }
]
}
],
"Description": []
}
]
}
],
"Description": []
}
]
}
子规则按从上到下首个匹配生效,所以顺序很重要:先拦该拦的,最后用通配兜底。
三、坑 2:运算符只有 in / matches / exists 三种能用
我在条件里写了 like、contains、==,全报错。查文档确认:站点加速规则引擎只支持 in(精确匹配)、matches(正则)、exists。像 like、contains、== 只在 Web 防护里能用,缓存规则里不行。
所以路径前缀别用通配符,要用正则:
# 匹配后台/登录/API 路径前缀
"${http.request.uri.path} matches '^/(wp-admin|wp-login|wp-json)/'"
# 判断用户是否登录(cookie 含关键字)
"${http.request.headers['cookie']} matches 'wordpress_logged_in'"
注意 file_extension 的值不带点,是 php 不是 .php。
四、坑 3:裸星号 * 在「规则引擎」入口直接报错
这是最隐蔽的一个。A 类缓存规则走「缓存规则」简化入口,* 通配能被接受(我的兜底规则就是 "Condition": "*",顺利通过了)。但当同样的 * 出现在「规则引擎」入口的 B 类规则里,直接报 Condition 语法错误,偏移量 0。
结论:「规则引擎」导入入口要求每条子规则条件都是真实表达式,不接受裸 *。要兜底就写成 "${http.request.host} in ['mlsha.cn', 'www.mlsha.cn']"。
五、坑 4:not 和 and 不能用,逻辑要绕道
我原本想写一条"HTML 页设 no-cache"的规则,条件用了 not (...) 加 and 组合,结果报 Branches[2].Condition 语法错误。
简化导入语法不支持 not/and。解决办法是正向匹配 + 兜底 fallthrough:先把"该拦的"逐条正向拦截(登录 cookie → NoCache),最后用通配兜底给匿名用户缓存,逻辑自然就对了,不用写否定。
六、坑 5:想改响应头?动作名是 ModifyResponseHeader
给静态资源加 Cache-Control: immutable、全站加安全头,这些属于"修改 HTTP 响应头"。我一开始写成自由文本 set Cache-Control——当然挂了。
正确动作名是 ModifyResponseHeader,参数结构是:
{
"Name": "ModifyResponseHeader",
"ModifyResponseHeaderParameters": {
"HeaderActions": [
{ "Action": "set", "Name": "X-Content-Type-Options", "Value": "nosniff" }
]
}
}
而且有个坑:条件读不到响应头(规则引擎的 Condition 只有 http.request.* 请求阶段变量)。所以"按 content-type 判断是不是 HTML"这个思路在导入 JSON 里行不通,得按请求后缀/路径来区分。
七、最终方案:两份文件,两个入口
踩完坑后,最稳的落地方式是分两个文件、分别导入:
| 文件 | 内容 | 导入入口 |
| edgeone-wordpress-cache-rules.json | A 类 7 条:静态资源 7 天 / php·后台·登录·feed NoCache / 登录用户 NoCache / 匿名 HTML(含首页)600s | 缓存规则 |
| edgeone-wordpress-headers-rules.json | B 类 3 条:安全头 / 静态 immutable / OPTIONS 预检 | 规则引擎 |
千万别把 ModifyResponseHeader 和 Cache 塞进同一个文件用「缓存规则」入口导入——那个入口只认 Cache,遇到响应头动作会再报语法错。
八、导入后怎么验证
# 匿名首页:第 1 次 MISS,第 2 次应变 HIT(边缘缓存生效)
curl -sI https://mlsha.cn/ | grep -iE "EO-Cache-Status"
# 登录页 / 后台:必须始终 MISS(不被缓存,防止串号)
curl -sI https://mlsha.cn/wp-login.php | grep -iE "EO-Cache-Status"
# 看响应头是否带安全头(B 类生效)
curl -sI https://mlsha.cn/ | grep -iE "X-Frame-Options|Referrer-Policy"
附录:拿来即用的完整配置
A 类(缓存规则入口)
{
"FormatVersion": "1.0",
"Rules": [
{
"RuleName": "WordPress 建站-mlsha.cn",
"Branches": [
{
"Condition": "${http.request.host} in ['mlsha.cn', 'www.mlsha.cn']",
"Actions": [],
"SubRules": [
{
"Branches": [
{
"Condition": "${http.request.file_extension} in ['gif', 'png', 'bmp', 'jpeg', 'jpg', 'tif', 'tiff', 'zip', 'wmv', 'swf', 'mp3', 'wma', 'rar', 'css', 'flv', 'mp4', 'txt', 'ico', 'js', 'webp', 'svg', 'woff', 'woff2', 'ttf', 'eot', 'otf', 'avif', 'webm', 'ogg', 'pdf']",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "CustomTime": { "Switch": "on", "IgnoreCacheControl": "on", "CacheTime": 604800 } } }
]
},
{
"Condition": "${http.request.file_extension} in ['php', 'aspx', 'jsp', 'asp', 'do', 'dwr', 'cgi', 'fcgi', 'action', 'ashx', 'axd']",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "NoCache": { "Switch": "on" } } }
]
},
{
"Condition": "${http.request.uri.path} matches '^/(wp-admin|wp-login|wp-json)/'",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "NoCache": { "Switch": "on" } } }
]
},
{
"Condition": "${http.request.uri.path} in ['/xmlrpc.php', '/wp-login.php']",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "NoCache": { "Switch": "on" } } }
]
},
{
"Condition": "${http.request.uri.path} matches '/feed/'",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "NoCache": { "Switch": "on" } } }
]
},
{
"Condition": "${http.request.headers['cookie']} matches 'wordpress_logged_in'",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "NoCache": { "Switch": "on" } } }
]
},
{
"Condition": "*",
"Actions": [
{ "Name": "Cache", "CacheParameters": { "CustomTime": { "Switch": "on", "IgnoreCacheControl": "on", "CacheTime": 600 } } }
]
}
],
"Description": []
}
]
}
],
"Description": []
}
]
}
B 类(规则引擎入口)
{
"FormatVersion": "1.0",
"Rules": [
{
"RuleName": "WordPress 响应头与安全头-mlsha.cn",
"Branches": [
{
"Condition": "${http.request.host} in ['mlsha.cn', 'www.mlsha.cn']",
"Actions": [],
"SubRules": [
{
"Branches": [
{
"Condition": "${http.request.host} in ['mlsha.cn', 'www.mlsha.cn']",
"Actions": [
{
"Name": "ModifyResponseHeader",
"ModifyResponseHeaderParameters": {
"HeaderActions": [
{ "Action": "set", "Name": "X-Content-Type-Options", "Value": "nosniff" },
{ "Action": "set", "Name": "X-Frame-Options", "Value": "SAMEORIGIN" },
{ "Action": "set", "Name": "Referrer-Policy", "Value": "strict-origin-when-cross-origin" }
]
}
}
]
},
{
"Condition": "${http.request.file_extension} in ['gif', 'png', 'bmp', 'jpeg', 'jpg', 'tif', 'tiff', 'zip', 'wmv', 'swf', 'mp3', 'wma', 'rar', 'css', 'flv', 'mp4', 'txt', 'ico', 'js', 'webp', 'svg', 'woff', 'woff2', 'ttf', 'eot', 'otf', 'avif', 'webm', 'ogg', 'pdf']",
"Actions": [
{
"Name": "ModifyResponseHeader",
"ModifyResponseHeaderParameters": {
"HeaderActions": [
{ "Action": "set", "Name": "Cache-Control", "Value": "public, max-age=31536000, immutable" }
]
}
}
]
},
{
"Condition": "${http.request.method} in ['OPTIONS']",
"Actions": [
{
"Name": "ModifyResponseHeader",
"ModifyResponseHeaderParameters": {
"HeaderActions": [
{ "Action": "set", "Name": "Access-Control-Max-Age", "Value": "86400" }
]
}
}
]
}
],
"Description": []
}
]
}
],
"Description": []
}
]
}
B 类第 3 条 OPTIONS 预检对纯同站博客没啥用(只有跨域才需要),不需要可以删,不影响其他两条。

暂无评论
要发表评论,您必须先 登录