During the previous round of WordPress 502 errors, I ultimately discovered that a batch of abnormally high-frequency requests carrying the Sogou web spider/4.0 User-Agent continuously bombarded the origin server, causing the 1-core CPU of the Alibaba Cloud ECS to stay fully loaded for extended periods and bringing the PHP-FPM queue to the verge of exhaustion.
After adding a *Sogou* User-Agent interception rule in EdgeOne, the 502 errors quickly disappeared, and the CPU, Load, and PHP-FPM queue returned to normal.
I initially thought this incident was mostly resolved.
However, after installing and restoring Alibaba Cloud host monitoring, I quickly noticed:
The CPU would still intermittently spike to 95%–100%.
This meant that the Sogou UA was merely a trigger for the previous severe outage, and a more fundamental issue remained behind the scenes.
After continuing the investigation this time, I ultimately narrowed the problem down to:
Front-end requests with arbitrary Query Strings can simultaneously reduce CDN cache hits and invalidate the W3 Total Cache Disk: Enhanced page cache, ultimately sending them straight into PHP-FPM.
This is a more worthwhile issue to address than simply blocking a specific crawler.
Moreover, it has also shifted the direction of my subsequent WordPress caching architecture optimization.
1. Sogou is already blocked, so why does the CPU still spike to 100%?
After restoring Alibaba Cloud host monitoring, I configured CPU, memory, disk, and public network bandwidth alarms for the ECS.
Soon after, I received multiple CPU Critical alarms.
For example:
2026-07-26 22:43 CPU = 100%
2026-07-27 01:34 CPU = 100%
2026-07-27 07:40 CPU = 99.26%These alarms were not caused by the CPU remaining consistently high all day.
Looking at the daily monitoring curve:
Normal CPU: approximately 20%–45%
Occasionally spikes suddenly
Reaches 95%–100% briefly
Then recoversMemory, on the other hand, remained consistently stable.

This first indicates:
This 1-core 2GB ECS is not under-resourced all day; rather, it experiences burst loads during specific time windows.
Therefore, I did not immediately upgrade to 2 cores and 4GB, but instead continued looking for the source of the CPU spikes.
2. Initially, I suspected WordPress Cron
The server currently disables WP-Cron triggered by visitor requests:
DISABLE_WP_CRON = 1It then actively executes once every 5 minutes via system Cron:
*/5 * * * * cd /data/wwwroot/www.shuijingwanwq.com && /usr/local/php/bin/php wp-cron.php >/dev/null 2>&1This frequency easily leads to the suspicion:
Executing wp-cron.php every 5 minutes
↓
Certain WordPress scheduled tasks are heavy
↓
CPU gets maxed outIn the WordPress Cron list, there is indeed a task that runs once every 5 minutes:
clarity_collect_batchBecause of this, Cron temporarily became the prime suspect.

3. Observing a round of WP-Cron in real time revealed it is actually very lightweight
To confirm, instead of modifying Cron, I directly observed the next round of wp-cron.php.
Around 10:00:
10:00:02 wp-cron not running
10:00:08
php wp-cron.php
CPU ≈ 31.2%
10:00:13
wp-cron already finishedThat is to say:
A normal WP-Cron run takes only a few seconds.
It clearly cannot explain:
CPU ≥95%
for several consecutive minutesTherefore, WP-Cron cannot serve as the primary explanation for the current CPU Critical alarms.
Of course, certain special Cron Hooks might still occasionally be heavy, but the troubleshooting priority should now be lowered.
4. After narrowing monitoring to 15 seconds, I confirmed it was indeed continuously fully loaded at 07:40
Next, I narrowed the Alibaba Cloud process monitoring time range to:
07:35~07:45The statistical cycle was also shortened to about 15 seconds.
This time, the CPU curve was very clear:
Around 07:37
CPU starts approaching 100%
Lasts for several minutes
After about 07:40
Gradually recoversSo:
07:40 CPU 99.26%was not a monitoring false alarm.
The server was indeed in a continuously fully loaded state at that time.

However, Alibaba Cloud’s process TopN did not fully capture the high-usage processes during these few minutes, so I went back to the server logs.
5. The real anomaly appeared in PHP-FPM
In the PHP-FPM main log, a very critical line quickly appeared:
[27-Jul-2026 07:36:16]
server reached max_children setting (7)That is to say:
pm.max_children = 7were all exhausted.
At the same time, starting from 07:36, there were a large number of:
index.php
executing too slow
3–4 secondsMoreover, these requests were not background tasks, nor were they Cron.
They were basically all front-end requests:
/index.php
/index.php?amp=1
/index.php?noamp=mobile
/index.php?nonamp=1
/index.php?query-62-page=...From 07:36 to 07:42, there were consistently PHP-FPM slow requests.

At this point, the failure chain had changed:
Large number of front-end requests
↓
Enter PHP-FPM
↓
All 7 workers busy
↓
Single-core CPU approaches 100%
↓
New requests continue to queueSo Cron was not the main contradiction behind this CPU spike.
6. The Slow Log further proves: WordPress is fully rendering these requests
In the PHP-FPM Slow Log, many real WordPress call chains can be seen.
Including:
Gutenberg
Polylang
W3 Total Cache
Redis Object Cache
Post Views Counter
WP_Query
mysqli_queryFor example, some of these requests go through:
W3 Total Cache
↓
Redis Object Cache
↓
Polylang
↓
Gutenberg block renderingThis shows that Redis Object Cache is indeed still working.
In addition, there are multiple calls to:
Post Views Counter
↓
pvc_get_most_viewed_posts()
↓
WP_Query
↓
mysqli_query()An important distinction needs to be made here:
Object Cache being effective does not mean Page Cache is effective.
Redis can reduce some database query and object retrieval costs, but as long as WordPress PHP has already started:
Plugins
Themes
Gutenberg
Polylang
Dynamic blocks
Database queriesstill need to continue executing.
And what can truly give PHP-FPM a complete break is:
A full-page Page Cache hit.
7. Only 687 requests in 8 minutes, yet enough to max out the server
I then analyzed:
07:35~07:42the Nginx logs for these 8 minutes.
Total number of requests:
687Status codes:
499 367
200 173
301 97
404 44
307 5
403 1Among them, 499 accounted for over half.
This usually means:
The client actively disconnected before the server finished responding.
Combined with the fact that PHP-FPM was already at 7/7 and request execution times exceeded 3 seconds, the 499 errors seem more like a consequence of server overload.
8. The traffic is not from a single IP, but clearly distributed
Among the top source IPs, no single IP made hundreds of requests.
The highest was only:
17
16
14
13
...But the sources were distributed across a large number of IPs and network segments.
Moreover, some of these IPs overlapped with the sources of the anomalous traffic carrying the Sogou UA from the previous day, for example:
1.71.146.14
122.246.2.213
1.71.147.97
222.79.116.117
1.71.147.232Therefore, there is a possibility:
After blocking the
*Sogou*UA the previous day, some automated requests continued to access the site using other User-Agents.
However, since User-Agents can be forged, I still do not intend to simply classify all these requests as coming from a specific bot.
What is truly more noteworthy is their access behavior.
9. User-Agent can no longer serve as a reliable sole condition for judgment
This batch of requests mixed:
Chrome
Firefox
Safari
Android Chromeas well as explicit bots:
MJ12bot
Googlebot
meta-externalagent
AmazonbotMany ordinary browser UAs even looked completely human.
So:
UA = Sogou
→ Blockcan only solve a very small category of cases.
If automated traffic directly disguises itself as:
Chrome
Safari
Firefoxcontinuing to patch by UA becomes meaningless.
10. What is truly noteworthy is the Query String
Among these 687 requests:
amp=1 106
noamp=mobile 33
nonamp=1 49
query-62-page 59There were also:
/page/119?query-62-page=111
/page/134/?query-62-page=135
...?amp=1
...?nonamp=1
...?noamp=mobileThis led me to start suspecting:
The real problem might not be a specific crawler, but rather that URLs with parameters can bypass the cache themselves.

11. So I tested the W3TC Page Cache directly
To confirm, I bypassed the CDN and requested the origin server directly via 127.0.0.1.
First, I tested the normal homepage:
/First time:
HTTP 200
TTFB = 9.03 secondsSecond time:
HTTP 200
TTFB = 0.11 secondsW3TC output:
Using page cache Disk: EnhancedAnd the second time:
Served fromThe time was completely consistent with the first time.
This proves that the Page Cache for normal URLs is working perfectly:
First time
WordPress fully generates
↓ Writes to Page Cache
Second time
Directly reuses HTML
12. After adding a parameter, the W3TC page cache is directly invalidated
Next, I tested:
/?amp=1First time:
TTFB = 11.11 secondsSecond time:
TTFB = 9.42 secondsW3TC explicitly output:
Disk: Enhanced (Requested URI contains query)And the two Served from times were different.
That is to say:
The second request did not reuse the page generated by the first request.
?noamp=mobile:
First time: 7.94 seconds
Second time: 9.01 seconds?nonamp=1:
First time: 7.70 seconds
Second time: 7.83 secondsThe situation was exactly the same.

At this point, the core mechanism behind this CPU spike is very clear.
13. The real danger is not amp, but “any parameter”
It is easy to initially think of:
amp
noamp
nonampand just block or redirect these three parameters.
But I quickly realized:
This treats the symptoms, not the root cause.
Because the attacker does not need to use these parameters at all.
They can easily generate:
?abc=1
?abc=2
?foo=123
?random=999
?test=xxxxxThe parameter names and values can theoretically vary infinitely.
As long as the current caching mechanism determines:
Query String exists
↓
W3TC Page Cache not reusedThen the attacker essentially possesses a very simple:
switch to force WordPress to render dynamically.
For a 1-core ECS, this is particularly dangerous.
14. This also explains why 687 requests can cause a noticeable impact
A normal cached request might only take:
0.1 secondsWhereas a dynamic request with a Query String takes:
7–11 secondsThe difference is nearly two orders of magnitude.
If 7 PHP-FPM Workers simultaneously process such requests:
7 workers
×
each lasting several secondsThe server will quickly enter:
pm.max_children = 7
↓
Requests queuing
↓
CPU 100%
↓
Responses slowing down
↓
499 errors increasingThis is no longer a question of “whether the traffic volume is large.”
Rather:
The same volume of traffic incurs completely different costs depending on whether it enters the server as cached requests or dynamic requests.
15. Therefore, I temporarily do not plan to upgrade to 2 cores and 4GB
After this analysis, I am even less inclined to immediately upgrade the ECS.
Because normally:
CPU around 20%–45%
Memory around 40%–50%The real 100% peaks mainly occur when the cache is heavily bypassed.
Upgrading to 2 cores and 4GB can increase capacity, but it cannot solve:
Arbitrary Query Strings
↓
Continuously generating dynamic WordPress requestsToday it was 687 requests.
In the future, it could easily become:
1000
2000
5000So a more reasonable sequence should be:
First fix cache bypassing
↓
Continue observing CPU
↓
Normal business still frequently maxes out
↓
Then consider upgrading the server16. I am also not going to deeply modify W3TC
Theoretically, I could study W3TC to make:
/article/
/article/?foo=1
/article/?foo=999share the same Page Cache.
For some known parameters, W3TC itself also has related mechanisms.
But the real problem is:
There can be an infinite number of parameters.
What I really want is:
Default to ignoring meaningless Query Strings
Only a few parameters that actually change content
act as exceptionsIf I were to modify:
advanced-cache.php
W3TC internal caching logic
Cache Keyto achieve this, not only would troubleshooting time continue to increase, but future W3TC and WordPress upgrades could also add maintenance costs.
This has already deviated from my goal this time:
Resolve the issue of the CPU being maxed out by anomalous requests as quickly as possible.
17. A solution more suited to me: let the CDN handle URL normalization
Therefore, I currently plan to handle this at the CDN layer.
Chinese site:
www.shuijingwanwq.com
→ Tencent EdgeOneEnglish site:
en.shuijingwanwq.com
→ CloudflareThe target logic is essentially the same.
For public pages confirmed to “not depend on Query String content”:
/article/
/article/?foo=1
/article/?foo=999The CDN layer should treat them as much as possible as:
The same cache objectIf the CDN already has a HIT:
Return directlyIf the CDN MISSes:
Clean meaningless parameters during origin pull
↓
Origin server receives clean URL
↓
W3TC Disk: Enhanced
↓
Continues to reuse normal Page CacheThus ultimately forming:
Client
/article/?random=123
↓ CDN
Ignores meaningless Query String
↓ HIT
Returns directly
↓ MISS
Origin pull:
/article/
↓ W3TC
Hits Disk: Enhanced Page Cache
↓ PHP-FPM
No need to fully execute WordPressThis better fits my current needs than modifying W3TC’s internal logic.
18. Why not just ignore Query Strings site-wide this time
There is also a very important boundary here.
Not all WordPress parameters are meaningless.
For example:
?p=123
?s=wordpress
?page_id=123
?preview=trueAs well as what appeared in this log:
query-62-pageSome of these parameters do actually change the page content.
Therefore, you cannot simply:
All Query Strings site-wide
→ Ignore allOtherwise, the following might occur:
/?s=PHP
/?s=WordPressreceiving the same cached page.
Even:
?p=100
?p=200could show incorrect content.
So the core of the subsequent configuration is not:
“Ignore all parameters.”
Rather:
Only create rules for URL types suitable for strong caching, where parameters usually should not change content.
My primary consideration right now is:
WordPress article detail pagesThese are the most numerous, highest-traffic, and most semantically explicit type of public pages.
19. Both EdgeOne and Cloudflare can adopt a similar approach
The Chinese site’s EdgeOne can handle this from two aspects:
Cache Key
+
Origin pull request parametersThe English site’s Cloudflare also has corresponding capabilities:
Cache Rule
+
Transform RuleTherefore, even though the two sites use different CDNs, they can ultimately maintain the same architectural logic:
www / EdgeOne
\
→ Query String normalization
/
en / Cloudflare
↓
Origin server receives clean public page URL
↓
W3TC Disk: EnhancedThis way, subsequent maintenance won’t turn into two completely different approaches.
20. The biggest takeaway from this troubleshooting is not actually finding a specific crawler
The initial problem appeared to be:
Sogou Spider
↓
CPU 100%
↓
502So it was natural to think:
Just block Sogou and it’ll be fine.
But after restoring monitoring, new CPU Critical alarms prompted me to dig deeper, ultimately revealing that what truly deserves long-term attention is:
Automated requests
↓
Large number of URLs with Query Strings
↓
CDN failing to fully absorb them
↓
W3TC Disk: Enhanced not reusing Page Cache due to Query String
↓
Requests entering PHP-FPM
↓
Fully executing WordPress
↓
Single request taking several seconds
↓
A small amount of concurrency is enough to max out a single-core serverCompared to “blocking a User-Agent,” this is clearly a more fundamental issue.
And it also gave me a renewed understanding of one point:
WordPress performance optimization cannot just look at whether caching exists, but also at whether requests can consistently fall onto the same cache key.
Caching exists, but if attackers or crawlers can continuously bypass it by arbitrarily adding parameters, then the protective power of the cache is greatly diminished.
21. Current stage: stop digging further into the server for now
At this point, I believe this round of issues has been located clearly enough.
I temporarily do not plan to:
Upgrade ECS
Increase pm.max_children
Modify W3TC internal code
Block parameters one by one
Block IPs one by one
Continue tracking a specific User-AgentWhat really needs to be done in the next stage is:
EdgeOne
↓
Verify Query String cache normalization for article detail pages
Cloudflare
↓
Configure equivalent rules
↓
Test URLs with random parameters
↓
Confirm CDN HIT
↓
When forcing a MISS, confirm W3TC can still hit the clean URL Page Cache
↓
Continue observing whether CPU Critical alarms significantly decreaseI plan to document these specific operations separately in the next article.
Because compared to this article’s “why the problem occurred,” the next article is better suited to fully document:
How to establish Query String cache normalization rules for WordPress article detail pages in EdgeOne and Cloudflare.
In closing
This troubleshooting actually went through several different stages:
502
↓
CPU 100%
↓
PHP-FPM queue exhausted
↓
Discovered anomalous Sogou UA traffic
↓
EdgeOne interception
↓
502 recovered
↓
Restored Alibaba Cloud host monitoring
↓
CPU Critical continued to appear
↓
Suspected WordPress Cron
↓
Cron tested normal
↓
Located PHP-FPM front-end slow requests
↓
Discovered a large number of URLs with Query Strings
↓
Tested W3TC directly
↓
Confirmed Query String causes Page Cache to not be reused
↓
Ultimately decided to handle cache normalization at the CDN layerSo this time, the key to truly solving the problem was not a specific command, nor a specific plugin.
Rather, it was progressively tracing:
“What is consuming the CPU”
down to:
“Why do these requests fall onto PHP.”
Now the answer is quite clear.
The next step is to translate this analysis into actual production configurations for EdgeOne and 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


发表回复