WordPress 配 EdgeOne 缓存规则 JSON

事情是这样的:最近检查站点 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 文档写,带了 StatusMaxAgeCacheKeyCachePrefresh 这些字段。结果控制台报"语法未通过"。

根因:"导入规则"这个入口只认一种动作 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 三种能用

我在条件里写了 likecontains==,全报错。查文档确认:站点加速规则引擎只支持 in(精确匹配)、matches(正则)、exists。像 likecontains== 只在 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.jsonA 类 7 条:静态资源 7 天 / php·后台·登录·feed NoCache / 登录用户 NoCache / 匿名 HTML(含首页)600s缓存规则
edgeone-wordpress-headers-rules.jsonB 类 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 预检对纯同站博客没啥用(只有跨域才需要),不需要可以删,不影响其他两条。

相关推荐

小米路由器AX3600 解锁SSH

小米AIoT路由器AX3600,是一台标准的Wi-Fi 6路由,采用高通第二代Wi-Fi 6方案,更加成熟。全套高通芯片,2.4GHz频段支持2T2R ...

OpenClaw 安全配置方案

1.  背景 OpenClaw作为一款功能强大的AI助手平台,在提供自动化服务的同时,其安全配置直接关系到企业数据安全和业务连续性 ...

暂无评论