前一轮 WordPress 502 故障中,我最终发现,一批携带 Sogou web spider/4.0 User-Agent 的异常高频请求持续冲击源站,导致只有 1 核 CPU 的阿里云 ECS 长时间满载,PHP-FPM 队列也接近耗尽。
在 EdgeOne 中增加 *Sogou* User-Agent 拦截规则以后,502 很快消失,CPU、Load 和 PHP-FPM 队列也恢复正常。
原本以为这次故障已经基本解决。
但安装并恢复阿里云主机监控以后,我很快发现:
CPU 仍然会不定时冲到 95%~100%。
这意味着,Sogou UA 只是上一轮严重故障的一个触发因素,服务器背后还存在另一个更基础的问题。
这次继续排查以后,我最终把问题收敛到了:
带任意 Query String 的前台请求,可以同时降低 CDN 缓存命中,并让 W3 Total Cache 的 Disk: Enhanced 页面缓存失效,最终直接进入 PHP-FPM。
这比单纯屏蔽某一个爬虫更值得处理。
而且,它也改变了我接下来优化 WordPress 缓存架构的方向。
一、Sogou 已经拦截,为什么 CPU 还是会冲到 100%?
恢复阿里云主机监控以后,我为 ECS 配置了 CPU、内存、磁盘和公网带宽报警。
随后很快收到多次 CPU Critical 告警。
例如:
2026-07-26 22:43 CPU = 100%
2026-07-27 01:34 CPU = 100%
2026-07-27 07:40 CPU = 99.26%而这些告警并不是 CPU 全天持续偏高。
从一天监控曲线来看:
平时 CPU:约 20%~45%
偶尔突然升高
短时间达到 95%~100%
随后恢复内存则一直比较稳定。

这首先说明:
当前这台 1 核 2GB ECS 并不是全天资源不足,而是某些时间段发生了突发负载。
因此我没有立刻升级到 2 核 4GB,而是继续找 CPU 峰值来源。
二、一开始我怀疑 WordPress Cron
服务器当前禁用了访客请求触发的 WP-Cron:
DISABLE_WP_CRON = 1然后通过系统 Cron 每 5 分钟主动执行一次:
*/5 * * * * cd /data/wwwroot/www.shuijingwanwq.com && /usr/local/php/bin/php wp-cron.php >/dev/null 2>&1这个频率很容易让人怀疑:
每 5 分钟执行 wp-cron.php
↓
某些 WordPress 定时任务很重
↓
CPU 被打满WordPress Cron 列表里确实还有一个每 5 分钟运行一次的任务:
clarity_collect_batch因此,Cron 一度成为第一嫌疑。

三、实时观察一轮 WP-Cron,却发现它非常轻
为了确认,我没有修改 Cron,而是直接观察下一轮 wp-cron.php。
10:00 左右:
10:00:02 wp-cron 未运行
10:00:08
php wp-cron.php
CPU ≈ 31.2%
10:00:13
wp-cron 已经结束也就是说:
一次普通 WP-Cron 运行只有几秒。
它显然无法解释:
CPU ≥95%
连续数分钟因此,WP-Cron 并不能作为当前 CPU Critical 的主要解释。
当然,某些特殊 Cron Hook 仍然可能偶尔比较重,但排查优先级已经应该下降。
四、把监控缩到 15 秒以后,确认 07:40 的确持续满载
接着,我把阿里云进程监控时间范围缩小到:
07:35~07:45统计周期也因此缩短到约 15 秒。
这一次 CPU 曲线非常清晰:
约 07:37
CPU 开始接近 100%
持续数分钟
约 07:40 后
逐渐恢复所以:
07:40 CPU 99.26%不是监控误报。
服务器当时的确处于持续满载状态。

不过阿里云的进程 TopN 并没有完整抓到这几分钟中的高占用进程,因此我继续回到服务器日志。
五、真正的异常出现在 PHP-FPM
PHP-FPM 主日志里,很快出现了非常关键的一行:
[27-Jul-2026 07:36:16]
server reached max_children setting (7)也就是说:
pm.max_children = 7已经全部用完。
与此同时,从 07:36 开始出现大量:
index.php
executing too slow
3~4 秒而且这些请求不是后台,也不是 Cron。
基本都是前台请求:
/index.php
/index.php?amp=1
/index.php?noamp=mobile
/index.php?nonamp=1
/index.php?query-62-page=...从 07:36 到 07:42,一直都有 PHP-FPM slow request。

这时,故障链已经发生变化:
大量前台请求
↓
进入 PHP-FPM
↓
7 个 Worker 全忙
↓
单核 CPU 接近 100%
↓
新请求继续排队所以 Cron 并不是这次 CPU 峰值的主要矛盾。
六、Slow Log 进一步证明:WordPress 正在完整渲染这些请求
PHP-FPM Slow Log 中可以看到很多真实的 WordPress 调用链。
包括:
Gutenberg
Polylang
W3 Total Cache
Redis Object Cache
Post Views Counter
WP_Query
mysqli_query例如其中一部分请求会经过:
W3 Total Cache
↓
Redis Object Cache
↓
Polylang
↓
Gutenberg 区块渲染说明 Redis Object Cache 确实仍然在工作。
另外还有多次调用:
Post Views Counter
↓
pvc_get_most_viewed_posts()
↓
WP_Query
↓
mysqli_query()这里需要特别区分:
Object Cache 生效,并不代表 Page Cache 生效。
Redis 可以减少一部分数据库查询和对象读取成本,但只要 WordPress PHP 已经启动:
插件
主题
Gutenberg
Polylang
动态区块
数据库查询仍然需要继续执行。
而真正能让 PHP-FPM 完全轻松下来的,是:
整页 Page Cache 命中。
七、8 分钟只有 687 个请求,却足以打满服务器
我随后分析了:
07:35~07:42这 8 分钟的 Nginx 日志。
总请求数:
687状态码:
499 367
200 173
301 97
404 44
307 5
403 1其中 499 超过一半。
这通常意味着:
客户端还没等服务器完成响应,就已经主动断开连接。
结合此时 PHP-FPM 已经 7/7、请求执行时间超过 3 秒,499 更像是服务器过载后的结果。
八、流量不是单一 IP,而是明显分散
来源 IP Top 中,并没有某一个 IP 发几百次请求。
最高的也只有:
17
16
14
13
...但来源分布在大量 IP 和网段中。
而且其中有一些 IP,与前一天携带 Sogou UA 的异常流量来源发生了重合,例如:
1.71.146.14
122.246.2.213
1.71.147.97
222.79.116.117
1.71.147.232因此存在一种可能:
前一天按
*Sogou*UA 拦截以后,部分自动化请求仍然通过其他 User-Agent 继续访问。
不过由于 User-Agent 可以伪造,我仍然不打算简单把这些请求全部归类成某个确定机器人。
真正更值得关注的是它们的访问行为。
九、User-Agent 已经无法作为可靠的唯一判断条件
这一批请求中混合了:
Chrome
Firefox
Safari
Android Chrome以及明确机器人:
MJ12bot
Googlebot
meta-externalagent
Amazonbot甚至很多普通浏览器 UA 看上去完全像真人。
所以:
UA = Sogou
→ Block只能解决很小的一类情况。
如果以后自动化流量直接伪装成:
Chrome
Safari
Firefox继续按 UA 打补丁就没有意义了。
十、真正值得注意的是 Query String
这 687 个请求里:
amp=1 106
noamp=mobile 33
nonamp=1 49
query-62-page 59同时还有:
/page/119?query-62-page=111
/page/134/?query-62-page=135
...?amp=1
...?nonamp=1
...?noamp=mobile这让我开始怀疑:
真正的问题可能不是某一种爬虫,而是带参数 URL 本身能够绕过缓存。

十一、于是直接测试 W3TC Page Cache
为了确认,我直接绕过 CDN,通过 127.0.0.1 请求源站。
先测试正常首页:
/第一次:
HTTP 200
TTFB = 9.03 秒第二次:
HTTP 200
TTFB = 0.11 秒W3TC 输出:
使用页面缓存 Disk: Enhanced而且第二次:
Served from时间与第一次完全一致。
这证明正常 URL 的 Page Cache 工作完全正常:
第一次
WordPress 完整生成
↓ 写入 Page Cache
第二次
直接复用 HTML
十二、加一个参数以后,W3TC 页面缓存直接失效
随后测试:
/?amp=1第一次:
TTFB = 11.11 秒第二次:
TTFB = 9.42 秒W3TC 明确输出:
Disk: Enhanced (Requested URI contains query)而且两次 Served from 时间不同。
也就是说:
第二次请求没有复用第一次生成的页面。
?noamp=mobile:
第一次:7.94 秒
第二次:9.01 秒?nonamp=1:
第一次:7.70 秒
第二次:7.83 秒情况完全一样。

至此,这次 CPU 峰值的核心机制已经非常清楚。
十三、真正危险的不是 amp,而是“任意参数”
一开始很容易想到:
amp
noamp
nonamp那就直接屏蔽或者重定向这三个参数。
但很快我意识到:
这是治标不治本。
因为攻击者根本不需要使用这些参数。
完全可以生成:
?abc=1
?abc=2
?foo=123
?random=999
?test=xxxxx参数名和参数值理论上都可以无限变化。
只要当前缓存机制判断:
存在 Query String
↓
W3TC Page Cache 不复用那么攻击者就相当于拥有一个非常简单的:
强制 WordPress 动态渲染开关。
对于一台 1 核 ECS 来说,这一点尤其危险。
十四、这也解释了为什么 687 个请求就能造成明显影响
正常缓存请求可能只需要:
0.1 秒而带 Query String 的动态请求:
7~11 秒差距接近两个数量级。
如果 7 个 PHP-FPM Worker 同时处理这样的请求:
7 个 Worker
×
每个持续数秒服务器很快就会进入:
pm.max_children = 7
↓
请求排队
↓
CPU 100%
↓
响应变慢
↓
499 增加这已经不再是“访问量大不大”的问题。
而是:
同样的访问量,以缓存请求和动态请求进入服务器,成本完全不同。
十五、因此我暂时不打算升级 2 核 4GB
这次分析以后,我反而更不倾向于马上升级 ECS。
因为平时:
CPU 约 20%~45%
内存约 40%~50%真正的 100% 峰值主要发生在缓存被大量穿透的时候。
升级到 2 核 4GB 可以提高承受能力,但不能解决:
任意 Query String
↓
持续制造动态 WordPress 请求今天是 687 个请求。
以后完全可能变成:
1000
2000
5000所以更合理的顺序应该是:
先修缓存穿透
↓
继续观察 CPU
↓
正常业务仍然频繁满载
↓
再考虑升级服务器十六、我也不准备深度修改 W3TC
理论上,可以研究 W3TC,让:
/article/
/article/?foo=1
/article/?foo=999共用同一份 Page Cache。
对于一些已知参数,W3TC 本身也有相关机制。
但真正的问题是:
参数可以有无限多个。
我真正想要的是:
默认忽略无意义 Query String
只有真正会改变内容的少数参数
作为例外如果为了实现这一点去修改:
advanced-cache.php
W3TC 内部缓存逻辑
Cache Key不仅排查时间会继续增加,而且以后 W3TC 升级、WordPress 升级还可能增加维护成本。
这已经偏离了我这次的目标:
尽快解决 CPU 被异常请求打满的问题。
十七、更适合我的方案:让 CDN 做 URL 规范化
因此,我目前准备把这件事放到 CDN 层。
中文站:
www.shuijingwanwq.com
→ Tencent EdgeOne英文站:
en.shuijingwanwq.com
→ Cloudflare目标逻辑基本一致。
对于确定“不依赖 Query String 内容”的公开页面:
/article/
/article/?foo=1
/article/?foo=999CDN 层尽量认为它们是:
同一个缓存对象如果 CDN 已经 HIT:
直接返回如果 CDN MISS:
回源时清理无意义参数
↓
源站收到干净 URL
↓
W3TC Disk: Enhanced
↓
继续复用正常 Page Cache于是最终形成:
客户端
/article/?random=123
↓ CDN
忽略无意义 Query String
↓ HIT
直接返回
↓ MISS
回源:
/article/
↓ W3TC
命中 Disk: Enhanced Page Cache
↓ PHP-FPM
不需要完整执行 WordPress这比去改 W3TC 内部逻辑更符合我现在的需求。
十八、为什么这次不直接全站忽略 Query String
这里还有一个非常重要的边界。
WordPress 并不是所有参数都没有意义。
例如:
?p=123
?s=wordpress
?page_id=123
?preview=true以及这次日志中出现的:
query-62-page其中一些参数确实会改变页面内容。
因此不能简单:
全站所有 Query String
→ 全部忽略否则可能出现:
/?s=PHP
/?s=WordPress拿到同一个缓存页面。
甚至:
?p=100
?p=200也出现错误内容。
所以后续配置的核心不是:
“所有参数都忽略。”
而是:
只针对适合强缓存、参数通常不应该改变内容的 URL 类型做规则。
我目前首先考虑的就是:
WordPress 文章详情页这是数量最多、访问价值最高,同时语义又最明确的一类公开页面。
十九、EdgeOne 与 Cloudflare 都可以采用类似思路
中文站的 EdgeOne 可以从两个方面处理:
Cache Key
+
回源请求参数英文站 Cloudflare 也有对应能力:
Cache Rule
+
Transform Rule因此即使两个站点使用的是不同 CDN,最终仍然可以保持同一种架构逻辑:
www / EdgeOne
\
→ Query String 规范化
/
en / Cloudflare
↓
源站收到干净公开页面 URL
↓
W3TC Disk: Enhanced这样后续维护也不会变成两套完全不同的思路。
二十、这次排查最大的收获,其实不是找到某一个爬虫
一开始的问题看起来是:
Sogou Spider
↓
CPU 100%
↓
502于是很自然地认为:
把 Sogou 挡掉就好了。
但恢复监控以后,新的 CPU Critical 让我继续往下查,最终发现真正值得长期处理的是:
自动化请求
↓
大量带 Query String 的 URL
↓
CDN 未能充分吸收
↓
W3TC Disk: Enhanced 因 Query String 不复用 Page Cache
↓
请求进入 PHP-FPM
↓
完整执行 WordPress
↓
单个请求耗时数秒
↓
少量并发即可打满单核服务器相比“封一个 User-Agent”,这显然是更底层的问题。
而它也让我重新理解了一点:
WordPress 性能优化不能只看有没有缓存,还要看请求是否能够稳定落到同一个缓存键上。
缓存存在,但如果攻击者或者爬虫可以通过随意添加参数不断绕过,那么缓存保护能力就会大幅下降。
二十一、当前阶段:先停止继续深挖服务器
到这里,我认为这一轮问题已经定位得足够清楚。
暂时不准备:
升级 ECS
提高 pm.max_children
修改 W3TC 内部代码
逐个封禁参数
逐个封禁 IP
继续追某一个 User-Agent下一阶段真正需要做的是:
EdgeOne
↓
验证文章详情页 Query String 缓存规范化
Cloudflare
↓
配置等价规则
↓
测试随机参数 URL
↓
确认 CDN HIT
↓
强制 MISS 时确认 W3TC 仍能命中干净 URL Page Cache
↓
继续观察 CPU Critical 是否明显减少这些具体操作,我准备放到下一篇文章中单独记录。
因为相比这篇的“问题为什么发生”,下一篇更适合完整记录:
如何在 EdgeOne 与 Cloudflare 中为 WordPress 文章详情页建立 Query String 缓存规范化规则。
写在最后
这次故障排查其实经历了几个不同阶段:
502
↓
CPU 100%
↓
PHP-FPM 队列耗尽
↓
发现 Sogou UA 异常流量
↓
EdgeOne 拦截
↓
502 恢复
↓
恢复阿里云主机监控
↓
继续出现 CPU Critical
↓
怀疑 WordPress Cron
↓
Cron 实测正常
↓
定位 PHP-FPM 前台慢请求
↓
发现大量带 Query String 的 URL
↓
直接测试 W3TC
↓
确认 Query String 导致 Page Cache 不复用
↓
最终决定从 CDN 层处理缓存规范化所以这一次,真正解决问题的关键不是某一条命令,也不是某一个插件。
而是逐步把:
“什么东西在占 CPU”
继续追问到:
“为什么这些请求会落到 PHP 上”。
现在答案已经比较明确。
下一步,就是把这个分析真正转化为 EdgeOne 和 Cloudflare 的生产配置。
需要长期技术维护或远程问题排查?
我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。
如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:
- ✅ PHP / Laravel / Yii2 老项目无人维护
- ✅ Go / Gin 后端接口需要排查或优化
- ✅ WordPress 网站访问慢、报错或插件冲突
- ✅ Nginx / MySQL / Redis / Linux 服务器异常
- ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
- ✅ 需要长期远程技术支持或兼职维护
更多介绍请查看:关于我 & 合作
微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

发表回复