Why WordPress attracts DDoS attacks
WordPress powers a large share of the web, and that popularity is precisely the problem: an attacker never has to study your site. Every install has the same file layout, the same login URL, the same REST namespace and the same expensive endpoints. One script written against WordPress works against millions of targets, yours included.
The second reason is cost per request. A static file is read from memory and gone in well under a millisecond. An uncached WordPress page is a PHP worker plus a WordPress bootstrap plus theme and plugin code plus a handful of database queries — often 200–600 ms of work. The attacker spends one cheap HTTP request; you spend a thousand times more. That is the cost asymmetry behind every Layer 7 attack, and WordPress hands it to attackers by default.
The third reason is what actually runs out. Almost nobody's WordPress site dies of saturated bandwidth. It dies because the PHP-FPM worker pool — typically somewhere between 5 and 50 processes — is fully occupied, so every subsequent visitor queues behind a flood and eventually gets a 502 or 504. A few hundred requests per second against an uncacheable path is enough. You do not need a headline-grabbing attack to be taken offline; you need a bored person with a script.
The endpoints attackers actually hit
| Endpoint | Why it is a target | Cheapest defense |
|---|---|---|
/xmlrpc.php | Pingback abuse and brute force — one request can carry many login attempts | Block at the edge unless you genuinely use it |
/wp-login.php, /wp-admin/ | Credential stuffing; never cacheable, and each hit boots WordPress | Rate limit plus challenge, restrict by country or IP |
/wp-cron.php | Fires scheduled jobs on demand — an attacker can run your cron thousands of times a second | Disable WP-Cron, run it from system cron |
/?s=… | Full-text search: database-heavy, uncacheable, and infinitely variable | Rate limit, short cache TTL, reject junk queries |
/wp-admin/admin-ajax.php | The catch-all endpoint every plugin uses; always PHP, often unauthenticated | Per-client rate limit |
/wp-json/wp/v2/users, /?author=1 | User enumeration that feeds the brute-force list | Restrict or disable if you do not publish author data |
/?anything=random | Cache-busting query strings crafted so every request misses cache | Ignore unknown query args in the cache key |
/cart, /checkout, ?add-to-cart= | WooCommerce paths are uncacheable by definition and write to the database | Rate limit, keep the origin unreachable |
Why a security plugin cannot stop a DDoS attack
A WordPress security plugin lives inside the thing being attacked. Walk the request path: the flood reaches your server, your web server accepts the connection, a PHP-FPM worker picks it up, WordPress bootstraps, plugins load — and only then does the plugin decide to block. You have already spent the exact resource the attacker is trying to exhaust. At a few thousand requests per second, "block the request" and "serve the request" cost you nearly the same thing.
This is not a criticism of plugins; it is a description of where they sit. Login lockouts, malware scanning, file integrity checks and 2FA are genuinely valuable, and none of them are volume defenses. The same reasoning applies, less severely, to .htaccess rules and server-level firewalls: blocking in nginx is far cheaper than blocking in PHP, but it is still your uplink, your CPU and your connection table absorbing the attack.
Mitigation has to happen before the request reaches PHP — and ideally before it reaches your server at all. That is the whole argument for filtering at the edge, covered in what DDoS mitigation is.
Step 1 — Close the amplifiers WordPress ships with
- XML-RPC. Unless you rely on it for remote publishing or a plugin that still needs it, block
/xmlrpc.phpoutright. It accepts many authentication attempts in a single request, which makes it the most efficient brute-force surface on the platform. - WP-Cron. By default WordPress checks its schedule on page loads, so an attack becomes a way to run your cron jobs continuously. Set
define('DISABLE_WP_CRON', true);inwp-config.phpand invokewp-cron.phpfrom a real system cron every few minutes instead. - Pingbacks and trackbacks. Turn them off in Settings → Discussion. They exist to let other servers make your server do work.
- Author enumeration. If your site does not publish author archives, block
?author=redirects and thewp-json/wp/v2/usersroute. Enumeration is the reconnaissance step that makes the subsequent credential attack efficient. - Unused REST routes. A headless or lightly used REST API should be reachable only where you actually need it.
The official WordPress hardening guide covers the rest of the baseline — updates, file permissions, least-privilege database users. None of it stops a flood, and all of it should be true anyway.
Step 2 — Make the common case free
Full-page caching at the edge is the single most effective DDoS defense available to a WordPress site, because it removes PHP from the request path entirely for anonymous visitors.
- Cache HTML for anonymous traffic, and bypass the cache when a
wordpress_logged_in_cookie or a WooCommerce cart cookie is present. Logged-in users get dynamic pages; the flood does not. - Ignore unknown query strings in the cache key, so
?utm_source=…and?x=8462resolve to the same cached object. Otherwise cache-busting parameters walk straight past your cache and into PHP. - Serve stale content on origin error (
stale-while-revalidate,stale-if-error). If your origin does buckle, visitors keep seeing the site instead of a 502. - Cache static assets aggressively with long TTLs and fingerprinted filenames.
The arithmetic is worth stating plainly: with a 60-second edge TTL, a flood of 5,000 requests per second against your homepage produces one origin request per minute. Everything else is served from the edge, which is why a CDN is attack armor and not just a speed tool.
Step 3 — Put a gate in front of login and admin
wp-login.php cannot be cached, is targeted constantly, and boots the full stack on every hit. Give it its own treatment: a strict per-client rate limit, a challenge for anonymous clients, and — if your team logs in from known places — a country or IP restriction on /wp-admin/. Add 2FA and stop using an account named admin. Credential stuffing and a Layer 7 flood are the same traffic pattern with different goals, and the same gate handles both.
Step 4 — Bound the expensive dynamic paths
Search, AJAX and commerce endpoints are where a small request volume buys a large amount of database work.
- Rate-limit
/?s=per client and cache successful searches briefly; reject empty and absurdly long queries outright. - Rate-limit
admin-ajax.php, which is where plugin code you did not write ends up doing unbounded work. - On WooCommerce, protect
?add-to-cart=,/cartand/checkoutwith per-client limits rather than caching — they must stay dynamic, so bound them instead. - Keep an object cache (Redis or Memcached) in front of the database so repeated queries stop being repeated work.
Step 5 — Make sure the edge cannot be bypassed
None of this matters if your server's real IP address is still reachable. Attackers pull DNS history, check certificate transparency logs and probe unproxied subdomains such as mail. or cpanel., then send the flood straight past your filtering layer. Firewall the origin so it accepts HTTP only from your mitigation network. The full checklist is in how to hide your origin IP address.
Step 6 — Size the origin for what still gets through
Set PHP-FPM max_children to what your RAM can actually support rather than an optimistic number, so overload degrades instead of swapping to death. Put a modest limit_req in nginx as a backstop. Cap slow query time. These will not survive an attack on their own — they decide whether the fraction that reaches you causes a slowdown or a crash.
If you are reading this because your site is down right now, work through how to stop a DDoS attack first and come back to the hardening afterwards.
How Itnetic protects WordPress sites
Itnetic sits in front of WordPress as a reverse proxy you enable with two DNS records — no plugin to install, no server changes, and nothing to maintain inside WordPress where an attacker's requests would already have cost you a worker.
Anonymous traffic meets an invisible challenge gate at the edge: real browsers pass without a CAPTCHA, and the scripted floods aimed at wp-login.php and xmlrpc.php never reach your PHP pool. Behavioral signatures and IP reputation filtering catch the clients that fake a browser user agent, while WAF rules let you allow, block or challenge by path, method, country or client fingerprint — which is how you lock /wp-admin/ to your own country in about a minute. Edge caching serves anonymous pages without touching your origin, and rate limits bound the paths that must stay dynamic: search, admin-ajax.php, cart and checkout. If you run WordPress headless, the wp-json routes can be marked as an API zone so machine clients get 429s instead of a challenge page they cannot solve.
Every verdict lands in per-request logs and analytics, so after an incident you can show exactly what was blocked and why. Attack traffic is never metered against your bandwidth quota — being attacked should not produce an invoice — and DDoS protection is included on every plan, including the free one.
Itnetic's DDoS protection goes live in about five minutes. If you want the background first, start with what DDoS mitigation is.