TL;DR Cloudflare cache hit ratio on a WordPress site dropped to 0.8% — the real alarm, not CPU or uptime.
Root cause: a single Singapore DigitalOcean IP flooding /xmlrpc.php with 288,493 POSTs in 24 hours, using system.multicall to brute-force hundreds of credentials per request.
with 288,493 POSTs in 24 hours, using to brute-force hundreds of credentials per request. Fix: Cloudflare WAF rule blocking /xmlrpc.php at the edge, plus WP Multitool's Frontend Optimizer disabling xmlrpc inside WordPress — defense in depth, both layers on by default in 1.1.19.
at the edge, plus WP Multitool's Frontend Optimizer disabling xmlrpc inside WordPress — defense in depth, both layers on by default in 1.1.19. Action for you: check your Cloudflare Top Paths weekly. If xmlrpc.php shows up in the top 3, you're already being hit.
Today I was glancing at Cloudflare analytics for one of my sites and something looked off. Cache hit ratio: 0.8%. That's not a typo. Zero point eight percent.
For a mostly-static WordPress site that should be sitting at 70–90%, 0.8% means something is very wrong. Either the cache rules are broken, or something is flooding the site with uncacheable traffic. Turned out to be the second one.
I pulled up the traffic breakdown and the answer was sitting right there. One IP from Singapore, 288,493 requests in 24 hours, all POSTs to /xmlrpc.php , all returning 200. That's about 12,000 requests per hour from a single DigitalOcean droplet. The site was still up because Cloudflare was absorbing most of it, but my origin was burning CPU on every single one of those requests.
Why 0.8% Cache Rate Was the Real Signal
Here's the thing about WordPress attacks on xmlrpc.php — they're almost invisible if you only watch uptime. The site loads fine for real visitors. The CPU graph might look a bit elevated. But the attack itself doesn't trigger any obvious alarm.
Cache rate is a great canary because xmlrpc.php is POST-only and marked dynamic. Every attack request counts against your cache rate denominator. When you see 288k uncacheable dynamic requests vs a few thousand normal cached ones, the ratio collapses.
... continue reading