Preventing WordPress Search Overload with Nginx

WordPress search is surprisingly expensive. Every query fires a database lookup with no caching layer in between, and when traffic spikes — whether from legitimate Google referrals or a bot scanning for vulnerabilities — those /search/keyword requests can quickly exhaust your PHP-FPM pool and database connections.

Caching helps everywhere else on your site, but search results change per query and per visitor. You can't cache them effectively. What you can do is limit how often a single visitor can hit the search endpoint.


The fix: rate limit the search location

Define a zone in your nginx.conf http { } block:

limit_req_zone $binary_remote_addr zone=search:10m rate=1r/s;

Apply it to the search location in your WordPress server block:

location /search {
    limit_req zone=search burst=3 nodelay;
    try_files $uri /index.php$is_args$args;
}

That's the entire fix — three lines. A visitor gets one search per second with a burst of three, and anything beyond that returns a 503. The try_files line passes the request to WordPress's index.php with query arguments intact, which is how WordPress processes search URLs.


Why try_files and not a rewrite?

Nginx processes directives in phases. Rewrites happen early — before access and rate-limiting checks. If you use a rewrite directive, the request gets rewritten to /index.php before limit_req ever sees it, and the rate limit applies to all PHP requests instead of just search. try_files runs in the content phase — after rate limiting — so the throttle fires on the original /search location first, and only passing requests reach WordPress.


Optional: map search to a dedicated error page

A 503 tells Google "try again later" — which is correct, but you might prefer a friendlier response for human visitors:

limit_req_status 429;

location /search {
    limit_req zone=search burst=3 nodelay;
    limit_req_status 429;
    try_files $uri /index.php$is_args$args;
}

error_page 429 =429 /search_busy.html;
location = /search_busy.html {
    root /var/www/mysite.com/errors;
    internal;
}

A 429 (Too Many Requests) with a custom page that says "search is busy, try again in a moment" is more useful than a blank 503.


Adjusting for your traffic

The values above are starting points. If your site sees organic flood traffic from Google referrals, you might raise the rate to 3r/s or increase the burst. If you're actively under attack, drop it to 1r/m. Watch your access logs and tune accordingly.


Where this fits in a larger rate-limiting strategy

Search is one of several endpoints that benefit from targeted rate limits. The WordPress on Nginx installation guide covers rate limiting the login page (/wp-login.php) and capping concurrent PHP connections per IP — both of which complement search throttling. The Securing Nginx and PHP guide covers broader rate-limiting patterns, including limit_req on deny blocks to throttle scanners before they reach a PHP handler.

Full documentation: ngx_http_limit_req_module

Technical Audit Summary

This guide is maintained as part of a modular, SSL-first framework. The rate-limiting pattern described here has been verified against live traffic across multiple WordPress sites.

Last Audit: May 2026
Environment: Debian Trixie (13)
Nginx: 1.30.2
PHP-FPM: 8.5.6

Compatibility: The limit_req module is included in all standard Nginx builds. The try_files vs rewrite phase distinction applies to all Nginx versions.

2026-05-21: Production audit — synced stack versions (Nginx 1.30.1, PHP-FPM 8.5.6). Added cross-links to the WordPress on Nginx installation guide (login rate limiting and PHP connection caps) and the Securing Nginx and PHP guide (broader rate-limiting patterns and botstop deny-block throttling).