Most WordPress security advice aims at the wrong threats — brute-force logins, SQL injection, plugin bugs. Those are real and well defended. Application-layer DoS is different: it does not exploit a vulnerability, it abuses the normal, intended behaviour of your stack — form submissions, search, API calls — and sends them faster than your server can cope. The result looks exactly like a hack, but nothing was stolen and nothing was modified. It ends when the attacker stops, and it can happen again tomorrow.

This is the layered defense we deploy, based on real attack testing. Read it alongside the mechanics in the Cloudflare myth, 167 bytes, and search as a DoS vector.

First, know your attack surface

An endpoint is dangerous only if it is unauthenticated, bypasses the CDN cache, and triggers expensive work. Most of your site fails that test — cached pages are served from the edge and never touch PHP. These are the ones that do not:

EndpointCDNRisk
Homepage, posts, pagesCACHEDNone
/?s= searchBYPASSHigh
Contact-form feedback (REST)DYNAMICHigh
WooCommerce cart / checkoutBYPASSHigh
/wp-login.php, /xmlrpc.phpBYPASSMedium

Layer 1 — the edge (stop it before PHP runs)

This is the highest-leverage layer: rules here cost zero origin resources. Add Cloudflare rate-limiting rules for each dynamic endpoint:

# Contact form REST API
(uri.path contains "/wp-json/contact-form-7") AND (method eq "POST")
  -> Block at 5 requests / 10s / IP

# Search
(uri.query contains "s=") AND (method eq "GET")
  -> Block at 10 requests / 30s / IP

# Login
(uri.path eq "/wp-login.php") AND (method eq "POST")
  -> Block at 5 requests / 60s / IP

And unless you actively use it, block XML-RPC outright:

(http.request.uri.path eq "/xmlrpc.php")  -> Block

Layer 2 — WordPress configuration

Close endpoints you do not need. Redirect search if it is unused; disable the contact-form REST route if your forms are embedded the older way; turn off XML-RPC. For example, to block XML-RPC at the web server:

<Files xmlrpc.php>
    Require all denied
</Files>

Every route you remove is one fewer thing to rate-limit.

Layer 3 — PHP-FPM and OPcache

Tuning the worker pool raises the exhaustion threshold (at a RAM cost), but the bigger win is making each request cheaper. Enable OPcache so bootstrap drops from ~300ms to ~50ms and workers free up several times faster, and set a hard execution timeout so one slow request cannot hold a worker forever:

pm.max_children = 50
pm.max_requests = 500       ; recycle workers, avoid leaks
max_execution_time = 15     ; kill runaway PHP
request_terminate_timeout = 20

More workers only ever buys you headroom — the attacker can add concurrency for free. Cheaper requests and edge rate limits are what actually change the maths.

Layer 4 — monitoring

Hardening without visibility is half a job. Watch Cloudflare analytics for a spike on a single path (that is the flood), put external uptime monitoring on a one-minute interval, and expose the PHP-FPM status page on localhost so you can see when active processes equals pm.max_children — the moment you are at capacity.

If you do only three things

  • Rate-limit the contact-form REST POST at the edge — 5 per 10s per IP.
  • Redirect or rate-limit search — it is core, on by default, and heavier than the form.
  • Block xmlrpc.php — most sites have no legitimate use for it.

Everything else is defense in depth. These three close the most commonly exploited surface. Reliability and integrity are the same promise from two angles — which is why we treat edge hardening as part of platform integrity and global reliability, not a separate checkbox. If you would like us to pressure-test your stack the way we tested these, get in touch.

Every configuration here is a defensive measure. Apply them to infrastructure you own or have explicit written permission to test. All findings were produced on authorized test infrastructure and submitted for responsible disclosure.