没有不值得去解决的问题,也没有不值得去学习的技术!

用 EdgeOne 与 Cloudflare 统一解决 WordPress 随机 Query String 缓存穿透:只优化 canonical 文章 URL

图3:EdgeOne 中标准文章 URL 与不同随机 Query String 均命中已有缓存

作者:

,

今天继续处理 WordPress 站点的 CDN 缓存问题。

此前排查服务器 CPU 与 PHP-FPM 压力时,我发现一个比较值得处理的场景:

Plaintext
https://www.shuijingwanwq.com/2026/07/27/20341/?abc=123
https://www.shuijingwanwq.com/2026/07/27/20341/?abc=456

文章内容实际上完全相同,但 URL 后面的 Query String 不同。如果 CDN 将这些请求视为不同缓存对象,就可能不断产生新的 MISS,并继续回源到 WordPress。

对于普通文章详情页而言,这些随机参数大多数没有实际意义。

但又不能简单地在全站范围内忽略 Query String,因为 WordPress 本身存在大量真正依赖参数的请求,例如:

Plaintext
/?s=wordpress
/?p=123
/?page_id=123

因此,这次的目标不是“全站忽略参数”,而是:

只对标准 WordPress 文章详情页进行 Query String 归一化。

我的中文站使用腾讯云 EdgeOne,英文站使用 Cloudflare,所以这次分别完成两套配置,并通过实际请求和 Nginx 日志验证。


一、最终确定的规则边界

我的文章固定链接结构为:

Plaintext
/YYYY/MM/DD/文章ID/

例如:

Plaintext
/2026/07/27/20341/

最初我考虑同时处理:

Plaintext
/2026/07/27/20341/
/2026/07/27/20341

后来实际测试发现,这样会让规则变得越来越复杂。

实际上,无尾斜杠 URL 本来就不是文章最终的 canonical URL。

WordPress / Polylang 会负责:

Plaintext
/2026/07/27/20341
        ↓ 301
/2026/07/27/20341/

因此最终决定:

CDN 只特殊优化末尾带 / 的 canonical 文章详情页。

也就是:

Plaintext
/YYYY/MM/DD/ID/

至于:

Plaintext
/YYYY/MM/DD/ID

继续由 WordPress / Polylang 完成正常的 canonical 301。

这样中文站和英文站可以保持完全相同的设计原则,而且半年以后重新查看 CDN 配置,也很容易理解。


二、中文站 EdgeOne 配置

中文站:

Plaintext
https://www.shuijingwanwq.com/

当前通过腾讯云 EdgeOne 加速。

1. 匹配文章详情页

在 EdgeOne 规则引擎中创建一条规则。

Host:

Plaintext
www.shuijingwanwq.com

URL Path 使用正则匹配:

Plaintext
^/[0-9]{4}/[0-9]{2}/[0-9]{2}/[0-9]+/$

例如:

Plaintext
/2026/07/27/20341/

会匹配。

而:

Plaintext
/2026/07/27/20341

不会匹配。

同样不会影响:

Plaintext
/
/?s=wordpress
/category/php/
/tag/wordpress/
/2026/07/27/20341/feed/
图1:EdgeOne 规则引擎中仅匹配 /YYYY/MM/DD/ID/ 形式的 WordPress canonical 文章详情页
图1:EdgeOne 规则引擎中仅匹配 /YYYY/MM/DD/ID/ 形式的 WordPress canonical 文章详情页

2. 自定义 Cache Key

第一个操作:

Plaintext
自定义 Cache Key
→ 查询字符串
→ 全部忽略

目标是让:

Plaintext
/2026/07/27/20341/
/2026/07/27/20341/?abc=111
/2026/07/27/20341/?abc=222

使用同一个缓存对象。


3. 回源请求删除 Query String

第二个操作:

Plaintext
回源请求参数设置
→ 查询字符串
→ 全部忽略

这样即使 EdgeOne MISS,客户端访问:

Plaintext
/2026/07/27/20341/?abc=123

EdgeOne 回源时也只请求:

Plaintext
/2026/07/27/20341/

随机参数不会继续进入源站。

图2:EdgeOne 同一规则中同时配置“自定义 Cache Key”和“回源请求参数设置”,两者均忽略 Query String
图2:EdgeOne 同一规则中同时配置“自定义 Cache Key”和“回源请求参数设置”,两者均忽略 Query String

三、EdgeOne Cache Key 实际验证

配置完成后,首先测试同一篇文章。

正常 URL:

Plaintext
https://www.shuijingwanwq.com/2026/07/27/20341/

以及不同随机参数:

Plaintext
?swq_eo_final=111
?swq_eo_final=222

测试结果:

Plaintext
带 /,无参数
HTTP/2 200
age: 4326
eo-cache-status: HIT

带 /,随机参数 111
HTTP/2 200
age: 4334
eo-cache-status: HIT

带 /,随机参数 222
HTTP/2 200
eo-cache-status: HIT

其中最明显的是:

Plaintext
无参数        age: 4326
参数 111      age: 4334

Age 连续增长。

而且一个此前没有访问过的新 Query String 第一次访问就是:

Plaintext
HIT

说明:

Plaintext
/20341/
/20341/?111
/20341/?222

已经归一到了已有文章缓存。

图3:EdgeOne 中标准文章 URL 与不同随机 Query String 均命中已有缓存
图3:EdgeOne 中标准文章 URL 与不同随机 Query String 均命中已有缓存

四、EdgeOne 回源参数也进行了实际验证

仅仅看到 HIT 还不够。

我还需要证明:

EdgeOne MISS 时,源站收到的请求确实已经删除 Query String。

于是换了一篇文章:

Plaintext
https://www.shuijingwanwq.com/2026/07/24/20084/

客户端实际请求:

Plaintext
/2026/07/24/20084/?swq_eo_final_origin_1785162581209501862=1

EdgeOne 返回:

Plaintext
HTTP/2 200
eo-cache-status: MISS

这说明本次发生了真实回源。

随后查询服务器 Nginx access log:

Plaintext
219.144.89.29 - - [27/Jul/2026:22:29:43 +0800]
"GET /2026/07/24/20084/ HTTP/1.1"
200 472412 "-"
"swq_eo_final_origin_1785162581209501862"

客户端发送的是:

Plaintext
/20084/?swq_eo_final_origin_...=1

但 Nginx 实际收到的是:

Plaintext
/20084/

因此可以确认:

Plaintext
客户端随机 Query String

EdgeOne MISS

删除 Query String

源站收到 canonical URL

这一部分不是根据控制台配置推测,而是通过源站日志实际验证。

图4:EdgeOne MISS 后 Nginx 日志显示源站实际收到的 URL 已经不包含随机 Query String
图4:EdgeOne MISS 后 Nginx 日志显示源站实际收到的 URL 已经不包含随机 Query String

五、为什么后来不再处理无尾斜杠 URL

测试过程中,我还测试了:

Plaintext
https://www.shuijingwanwq.com/2026/07/27/20341

结果:

Plaintext
HTTP/2 301
x-redirect-by: Polylang
location: https://www.shuijingwanwq.com/2026/07/27/20341/
eo-cache-status: MISS

带随机参数时:

Plaintext
/20341?swq_eo_noslash=...

同样由 Polylang 301。

最初我考虑增加一条 EdgeOne 规则,让这种 URL 直接在边缘节点补 /

但继续考虑以后,我觉得没有必要。

正常链路完全可以是:

Plaintext
/20341?abc=123

WordPress / Polylang 301

/20341/?abc=123

EdgeOne canonical 文章规则

命中 /20341/ 缓存

代价只是第一次无尾斜杠请求需要 WordPress 生成一次 301。

目前没有证据证明这类请求已经多到足以造成明显服务器压力。

为了消除一次 301,再增加额外 CDN 重定向规则,会增加长期维护成本。

所以最终原则是:

只保护真正的 canonical URL,不为了极小概率场景继续堆积 CDN 规则。


六、英文站 Cloudflare 配置

英文站:

Plaintext
https://en.shuijingwanwq.com/

使用 Cloudflare。

这里实现同一个目标:

Plaintext
/YYYY/MM/DD/ID/?随机参数

最终与:

Plaintext
/YYYY/MM/DD/ID/

共用缓存。


1. 使用 URL Rewrite Rule

Cloudflare 免费套餐的规则界面没有直接提供我需要的正则匹配能力,因此最终使用 Rules Language 的字符串函数限定文章路径。

最终条件大致为:

Plaintext
(
  http.host eq "en.shuijingwanwq.com"
  and starts_with(http.request.uri.path, "/20")
  and substring(http.request.uri.path, 5, 6) eq "/"
  and substring(http.request.uri.path, 8, 9) eq "/"
  and substring(http.request.uri.path, 11, 12) eq "/"
  and ends_with(http.request.uri.path, "/")
  and substring(http.request.uri.path, 12, -1) ne ""
  and not (substring(http.request.uri.path, 12, -1) contains "/")
  and http.request.uri.query ne ""
)

目标仍然只是:

Plaintext
/2026/07/14/19522/

而不是:

Plaintext
/2026/07/14/19522
/2026/07/14/19522/feed/
/2026/07/14/19522/embed/
图5:Cloudflare URL Rewrite Rule 中限定英文站 canonical 文章详情页
图5:Cloudflare URL Rewrite Rule 中限定英文站 canonical 文章详情页

2. 不修改 Path,只删除 Query

动作配置:

Plaintext
路径:
保留

查询:
重写到
Static
空值

也就是:

Plaintext
/2026/07/14/19522/?abc=123

在 Cloudflare 内部被处理为:

Plaintext
/2026/07/14/19522/

路径本身不发生改变。

图6:Cloudflare URL Rewrite Rule 保留 Path,仅将 Query 重写为空值
图6:Cloudflare URL Rewrite Rule 保留 Path,仅将 Query 重写为空值

七、Cloudflare 回源删除 Query String 验证

第一次测试带随机参数:

Plaintext
/2026/07/14/19522/?swq_cf_slash=...

Cloudflare 返回:

Plaintext
HTTP/2 200
cf-cache-status: MISS

随后在服务器 Nginx access log 中找到:

Plaintext
162.158.110.160 - - [27/Jul/2026:22:09:39 +0800]
"GET /2026/07/14/19522/ HTTP/2.0"
200 50901 "-" "curl/8.18.0"

再次证明:

Plaintext
客户端:
/19522/?随机参数

        ↓ Cloudflare

源站:
/19522/

Query String 没有进入 WordPress。

图7:Cloudflare MISS 请求对应的 Nginx 日志中已经看不到客户端随机参数
图7:Cloudflare MISS 请求对应的 Nginx 日志中已经看不到客户端随机参数

八、Cloudflare 是否真的共用缓存

仅仅证明“回源没有参数”还不够。

更关键的问题是:

Plaintext
/19522/

和:

Plaintext
/19522/?abc=123

是否真的使用同一份 Cloudflare CDN 缓存

于是进行了连续测试。

结果:

Plaintext
标准 URL 第一次
MISS

标准 URL 第二次
HIT
age: 5

参数 111
HIT
age: 8

参数 222
HIT
age: 11

参数 333
HIT
age: 16

标准 URL 再次
HIT
age: 19

而且这些请求全部落到:

Plaintext
AMS

同一个 Cloudflare 节点。

这个结果已经非常明确:

Plaintext
/19522/
/19522/?111
/19522/?222
/19522/?333

全部命中了同一份已有缓存。

特别是:

Plaintext
111
222
333

都是此前没有访问过的新参数,却第一次请求就直接得到:

Plaintext
CF-Cache-Status: HIT

并且:

Plaintext
Age: 5 → 8 → 11 → 16 → 19

持续增长。

因此没有必要再额外创建一条 Cloudflare Cache Rule。

一条 URL Rewrite Rule 已经完成了当前需求。

图8:Cloudflare 不同 Query String 连续 HIT,Age 持续增长,证明与标准 URL 共用缓存
图8:Cloudflare 不同 Query String 连续 HIT,Age 持续增长,证明与标准 URL 共用缓存

九、Cloudflare 无尾斜杠端到端验证

最后还测试了:

Plaintext
/2026/07/14/19522?随机参数

让 curl 自动跟随 301。

结果第一跳:

Plaintext
HTTP/2 301

location:
https://en.shuijingwanwq.com/2026/07/14/19522/?swq_cf_e2e_1785162314320989211=1

x-redirect-by: Polylang
cf-cache-status: MISS

也就是:

Plaintext
/19522?随机参数

Polylang

/19522/?随机参数

第二跳:

Plaintext
HTTP/2 200
age: 524
cf-cache-status: HIT

完整链路变成:

Plaintext
/19522?random=1

Polylang 301

/19522/?random=1

Cloudflare URL Rewrite

Query String 被归一化

命中 /19522/ 已有缓存

因此,即使不专门优化无尾斜杠 URL,最终仍然会回到已经优化好的 canonical CDN 缓存路径。

图9:Cloudflare 无尾斜杠文章 URL 经 Polylang 301 后,第二跳直接命中已有 CDN 缓存
图9:Cloudflare 无尾斜杠文章 URL 经 Polylang 301 后,第二跳直接命中已有 CDN 缓存

十、中文和英文站最终统一成同一种设计

完成这轮调整后,两套 CDN 的实现方式虽然不同,但设计思想已经完全一致。

中文站 EdgeOne:

Plaintext
www.shuijingwanwq.com

/YYYY/MM/DD/ID/

Query String 不参与 Cache Key

不同参数共用缓存
        ↓ MISS
回源删除 Query String

英文站 Cloudflare:

Plaintext
en.shuijingwanwq.com

/YYYY/MM/DD/ID/?参数

URL Rewrite 删除 Query String

与 canonical URL 共用缓存
        ↓ MISS
源站收到无参数 URL

而无尾斜杠:

Plaintext
/YYYY/MM/DD/ID

两边都保持 WordPress / Polylang 原来的 canonical 行为:

Plaintext
/ID
↓ 301
/ID/

不再为了这一层增加 CDN 特殊逻辑。


十一、这次配置中我最终更看重“简单”

这次配置过程中其实有很多机会继续优化。

比如:

  • EdgeOne 可以额外做无尾斜杠边缘 301;
  • Cloudflare 可以增加更多 Cache Rule;
  • 可以进一步把各种 /feed//embed/、特殊参数组合全部拆开处理。

但是 CDN 配置不是越多越好。

规则越来越多以后,会产生另一个问题:

半年以后自己都不容易确认某个请求到底命中了哪一条规则。

所以这次最终选择了一条很明确的边界:

CDN 只负责 canonical 文章 URL 的 Query String 缓存归一化。

WordPress 自己能正常解决的问题,例如无尾斜杠 canonical 301,就继续让 WordPress 解决。

这种设计可能不是理论上的“极致优化”,但更容易维护,也更符合我现在这个个人 WordPress 站点的实际需求。


十二、测试过程中仍然出现 Cloudflare 522 / 525

测试英文站期间,还多次遇到了:

Plaintext
522
525

而且此前发布文章时也已经发现过类似现象:

第一次访问可能得到 Cloudflare 522 / 525,再请求一次又恢复正常。

本次测试中也出现过类似情况。

例如家中电脑访问时,有请求落到:

Plaintext
LAX
LAS

并出现 522 / 525。

而后来直接从服务器终端请求时,请求落到:

Plaintext
AMS

完整的:

Plaintext
301 → HIT

链路又能正常完成。

目前服务器检查到:

Plaintext
load average: 0.98, 0.90, 1.15

内存:

Plaintext
总计:1.9 GiB
可用:889 MiB

443 端口也由 Nginx 正常监听。

因此,现阶段还不能仅凭 522 / 525 就确定一定是 ECS 硬件不足。

当然,我仍然认为服务器性能可能是其中一个因素,后续甚至可能最终需要升级 ECS。

但这个问题暂时不在本次继续排查。

这一次先完成:

EdgeOne + Cloudflare 的 WordPress 文章 Query String 缓存规范化。

522 / 525 留到后面作为单独的问题分析,避免把 CDN 缓存规则和源站稳定性混在一起。


十三、最终结果

至此,两个前台域名的文章详情页已经实现:

Plaintext
随机 Query String

不会持续制造新的文章缓存对象

canonical URL 共用 CDN 缓存

并且在真正需要回源时:

Plaintext
带随机参数的请求

CDN

源站收到无参数 canonical URL

最重要的是,这些结论都不是只根据 CDN 控制台配置判断,而是通过:

Plaintext
HIT / MISS
Age
301
Location
CF-Ray
Nginx access log

一步一步实际验证出来的。

最终规则也从最初“尽可能覆盖所有情况”,收敛成了一个非常容易理解的模型:

Plaintext
只有 /YYYY/MM/DD/ID/
才属于 CDN 文章详情页特殊优化范围。

对于我现在的网站来说,我更喜欢这个结果。

因为它不仅解决了随机 Query String 带来的缓存穿透问题,也没有把 CDN 配置变成另一套难以维护的复杂系统。

而至于偶发的 Cloudflare 522 / 525,则留到下一阶段再单独决定:究竟继续优化服务器配置,还是直接升级 ECS 硬件。

Linux 服务器运维、部署与线上故障排查

如果你的网站或后端服务部署在 Linux 服务器上,遇到访问异常、Nginx 配置问题、MySQL / Redis 异常、Docker 服务不可用、磁盘占满、CPU / 内存过高等问题,可以联系我做一次远程排查。

适合以下场景:
✅ 网站打不开或访问不稳定
✅ Nginx / PHP-FPM 配置异常
✅ MySQL / Redis 性能或连接问题
✅ Docker 服务部署与维护
✅ 服务器迁移与环境配置
✅ CPU / 内存 / 磁盘异常排查

服务内容:
✅ Linux 环境检查
✅ 网站部署与迁移
✅ Nginx / PHP-FPM / MySQL / Redis 排查
✅ Docker 配置与维护
✅ 服务器性能分析
✅ 长期远程运维支持

如需咨询,请联系我,并注明:Linux 运维咨询

联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理