今天继续处理 WordPress 站点的 CDN 缓存问题。
此前排查服务器 CPU 与 PHP-FPM 压力时,我发现一个比较值得处理的场景:
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 本身存在大量真正依赖参数的请求,例如:
/?s=wordpress
/?p=123
/?page_id=123
因此,这次的目标不是“全站忽略参数”,而是:
只对标准 WordPress 文章详情页进行 Query String 归一化。
我的中文站使用腾讯云 EdgeOne,英文站使用 Cloudflare,所以这次分别完成两套配置,并通过实际请求和 Nginx 日志验证。
一、最终确定的规则边界
我的文章固定链接结构为:
/YYYY/MM/DD/文章ID/
例如:
/2026/07/27/20341/
最初我考虑同时处理:
/2026/07/27/20341/
/2026/07/27/20341
后来实际测试发现,这样会让规则变得越来越复杂。
实际上,无尾斜杠 URL 本来就不是文章最终的 canonical URL。
WordPress / Polylang 会负责:
/2026/07/27/20341
↓ 301
/2026/07/27/20341/
因此最终决定:
CDN 只特殊优化末尾带
/的 canonical 文章详情页。
也就是:
/YYYY/MM/DD/ID/
至于:
/YYYY/MM/DD/ID
继续由 WordPress / Polylang 完成正常的 canonical 301。
这样中文站和英文站可以保持完全相同的设计原则,而且半年以后重新查看 CDN 配置,也很容易理解。
二、中文站 EdgeOne 配置
中文站:
https://www.shuijingwanwq.com/
当前通过腾讯云 EdgeOne 加速。
1. 匹配文章详情页
在 EdgeOne 规则引擎中创建一条规则。
Host:
www.shuijingwanwq.com
URL Path 使用正则匹配:
^/[0-9]{4}/[0-9]{2}/[0-9]{2}/[0-9]+/$
例如:
/2026/07/27/20341/
会匹配。
而:
/2026/07/27/20341
不会匹配。
同样不会影响:
/
/?s=wordpress
/category/php/
/tag/wordpress/
/2026/07/27/20341/feed/

/YYYY/MM/DD/ID/ 形式的 WordPress canonical 文章详情页2. 自定义 Cache Key
第一个操作:
自定义 Cache Key
→ 查询字符串
→ 全部忽略
目标是让:
/2026/07/27/20341/
/2026/07/27/20341/?abc=111
/2026/07/27/20341/?abc=222
使用同一个缓存对象。
3. 回源请求删除 Query String
第二个操作:
回源请求参数设置
→ 查询字符串
→ 全部忽略
这样即使 EdgeOne MISS,客户端访问:
/2026/07/27/20341/?abc=123
EdgeOne 回源时也只请求:
/2026/07/27/20341/
随机参数不会继续进入源站。

三、EdgeOne Cache Key 实际验证
配置完成后,首先测试同一篇文章。
正常 URL:
https://www.shuijingwanwq.com/2026/07/27/20341/
以及不同随机参数:
?swq_eo_final=111
?swq_eo_final=222
测试结果:
带 /,无参数
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
其中最明显的是:
无参数 age: 4326
参数 111 age: 4334
Age 连续增长。
而且一个此前没有访问过的新 Query String 第一次访问就是:
HIT
说明:
/20341/
/20341/?111
/20341/?222
已经归一到了已有文章缓存。

四、EdgeOne 回源参数也进行了实际验证
仅仅看到 HIT 还不够。
我还需要证明:
EdgeOne MISS 时,源站收到的请求确实已经删除 Query String。
于是换了一篇文章:
https://www.shuijingwanwq.com/2026/07/24/20084/
客户端实际请求:
/2026/07/24/20084/?swq_eo_final_origin_1785162581209501862=1
EdgeOne 返回:
HTTP/2 200
eo-cache-status: MISS
这说明本次发生了真实回源。
随后查询服务器 Nginx access log:
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"
客户端发送的是:
/20084/?swq_eo_final_origin_...=1
但 Nginx 实际收到的是:
/20084/
因此可以确认:
客户端随机 Query String
↓
EdgeOne MISS
↓
删除 Query String
↓
源站收到 canonical URL
这一部分不是根据控制台配置推测,而是通过源站日志实际验证。

五、为什么后来不再处理无尾斜杠 URL
测试过程中,我还测试了:
https://www.shuijingwanwq.com/2026/07/27/20341
结果:
HTTP/2 301
x-redirect-by: Polylang
location: https://www.shuijingwanwq.com/2026/07/27/20341/
eo-cache-status: MISS
带随机参数时:
/20341?swq_eo_noslash=...
同样由 Polylang 301。
最初我考虑增加一条 EdgeOne 规则,让这种 URL 直接在边缘节点补 /。
但继续考虑以后,我觉得没有必要。
正常链路完全可以是:
/20341?abc=123
↓
WordPress / Polylang 301
↓
/20341/?abc=123
↓
EdgeOne canonical 文章规则
↓
命中 /20341/ 缓存
代价只是第一次无尾斜杠请求需要 WordPress 生成一次 301。
目前没有证据证明这类请求已经多到足以造成明显服务器压力。
为了消除一次 301,再增加额外 CDN 重定向规则,会增加长期维护成本。
所以最终原则是:
只保护真正的 canonical URL,不为了极小概率场景继续堆积 CDN 规则。
六、英文站 Cloudflare 配置
英文站:
https://en.shuijingwanwq.com/
使用 Cloudflare。
这里实现同一个目标:
/YYYY/MM/DD/ID/?随机参数
最终与:
/YYYY/MM/DD/ID/
共用缓存。
1. 使用 URL Rewrite Rule
Cloudflare 免费套餐的规则界面没有直接提供我需要的正则匹配能力,因此最终使用 Rules Language 的字符串函数限定文章路径。
最终条件大致为:
(
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 ""
)
目标仍然只是:
/2026/07/14/19522/
而不是:
/2026/07/14/19522
/2026/07/14/19522/feed/
/2026/07/14/19522/embed/

2. 不修改 Path,只删除 Query
动作配置:
路径:
保留
查询:
重写到
Static
空值
也就是:
/2026/07/14/19522/?abc=123
在 Cloudflare 内部被处理为:
/2026/07/14/19522/
路径本身不发生改变。

七、Cloudflare 回源删除 Query String 验证
第一次测试带随机参数:
/2026/07/14/19522/?swq_cf_slash=...
Cloudflare 返回:
HTTP/2 200
cf-cache-status: MISS
随后在服务器 Nginx access log 中找到:
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"
再次证明:
客户端:
/19522/?随机参数
↓ Cloudflare
源站:
/19522/
Query String 没有进入 WordPress。

八、Cloudflare 是否真的共用缓存
仅仅证明“回源没有参数”还不够。
更关键的问题是:
/19522/
和:
/19522/?abc=123
是否真的使用同一份 Cloudflare CDN 缓存?
于是进行了连续测试。
结果:
标准 URL 第一次
MISS
标准 URL 第二次
HIT
age: 5
参数 111
HIT
age: 8
参数 222
HIT
age: 11
参数 333
HIT
age: 16
标准 URL 再次
HIT
age: 19
而且这些请求全部落到:
AMS
同一个 Cloudflare 节点。
这个结果已经非常明确:
/19522/
/19522/?111
/19522/?222
/19522/?333
全部命中了同一份已有缓存。
特别是:
111
222
333
都是此前没有访问过的新参数,却第一次请求就直接得到:
CF-Cache-Status: HIT
并且:
Age: 5 → 8 → 11 → 16 → 19
持续增长。
因此没有必要再额外创建一条 Cloudflare Cache Rule。
一条 URL Rewrite Rule 已经完成了当前需求。

九、Cloudflare 无尾斜杠端到端验证
最后还测试了:
/2026/07/14/19522?随机参数
让 curl 自动跟随 301。
结果第一跳:
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
也就是:
/19522?随机参数
↓
Polylang
↓
/19522/?随机参数
第二跳:
HTTP/2 200
age: 524
cf-cache-status: HIT
完整链路变成:
/19522?random=1
↓
Polylang 301
↓
/19522/?random=1
↓
Cloudflare URL Rewrite
↓
Query String 被归一化
↓
命中 /19522/ 已有缓存
因此,即使不专门优化无尾斜杠 URL,最终仍然会回到已经优化好的 canonical CDN 缓存路径。

十、中文和英文站最终统一成同一种设计
完成这轮调整后,两套 CDN 的实现方式虽然不同,但设计思想已经完全一致。
中文站 EdgeOne:
www.shuijingwanwq.com
/YYYY/MM/DD/ID/
↓
Query String 不参与 Cache Key
↓
不同参数共用缓存
↓ MISS
回源删除 Query String
英文站 Cloudflare:
en.shuijingwanwq.com
/YYYY/MM/DD/ID/?参数
↓
URL Rewrite 删除 Query String
↓
与 canonical URL 共用缓存
↓ MISS
源站收到无参数 URL
而无尾斜杠:
/YYYY/MM/DD/ID
两边都保持 WordPress / Polylang 原来的 canonical 行为:
/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
测试英文站期间,还多次遇到了:
522
525
而且此前发布文章时也已经发现过类似现象:
第一次访问可能得到 Cloudflare 522 / 525,再请求一次又恢复正常。
本次测试中也出现过类似情况。
例如家中电脑访问时,有请求落到:
LAX
LAS
并出现 522 / 525。
而后来直接从服务器终端请求时,请求落到:
AMS
完整的:
301 → HIT
链路又能正常完成。
目前服务器检查到:
load average: 0.98, 0.90, 1.15
内存:
总计:1.9 GiB
可用:889 MiB
443 端口也由 Nginx 正常监听。
因此,现阶段还不能仅凭 522 / 525 就确定一定是 ECS 硬件不足。
当然,我仍然认为服务器性能可能是其中一个因素,后续甚至可能最终需要升级 ECS。
但这个问题暂时不在本次继续排查。
这一次先完成:
EdgeOne + Cloudflare 的 WordPress 文章 Query String 缓存规范化。
522 / 525 留到后面作为单独的问题分析,避免把 CDN 缓存规则和源站稳定性混在一起。
十三、最终结果
至此,两个前台域名的文章详情页已经实现:
随机 Query String
↓
不会持续制造新的文章缓存对象
↓
canonical URL 共用 CDN 缓存
并且在真正需要回源时:
带随机参数的请求
↓
CDN
↓
源站收到无参数 canonical URL
最重要的是,这些结论都不是只根据 CDN 控制台配置判断,而是通过:
HIT / MISS
Age
301
Location
CF-Ray
Nginx access log
一步一步实际验证出来的。
最终规则也从最初“尽可能覆盖所有情况”,收敛成了一个非常容易理解的模型:
只有 /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

发表回复