WordPress Search Rate Limiter
Limits frontend WordPress search requests per IP address with configurable thresholds, proxy handling, caching, and a countdown page.
by Amirreza Shayesteh Far · github.com/amirrezashf/wordpress-search-rate-limiter · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/amirrezashf/wordpress-search-rate-limiter/archive/refs/heads/main.zipReadme
WordPress Search Rate Limiter
Limits frontend WordPress search requests per IP address with configurable thresholds, trusted proxy handling, caching, and an HTTP 429 countdown page.
Description
WordPress Search Rate Limiter protects the standard WordPress frontend search endpoint from excessive repeated requests.
By default, each IP address may submit up to four search requests during a rolling 60-second fixed window. The fifth request is blocked until the current window expires.
Blocked requests receive:
- HTTP status
429 Too Many Requests - A
Retry-Afterresponse header - Cache-prevention headers
- A responsive countdown page
- Automatic page reload after expiration
The plugin applies only to frontend requests containing the standard WordPress ?s= search parameter.
Features
- IP-based frontend search rate limiting
- Four accepted searches per 60 seconds by default
- Blocks the fifth request
- HTTP
429 Too Many Requestsresponse Retry-Afterresponse header- Countdown timer
- Automatic retry after expiration
- Persistent Object Cache support
- WordPress Transients fallback
- Trusted proxy and CDN support
- IPv4 and IPv6 support
- Exact proxy IP and CIDR support
- Ignores admin requests
- Ignores REST API requests
- Ignores WordPress AJAX requests
- Ignores XML-RPC requests
- Ignores WP-CLI and command-line execution
- Configurable limits and response text
- No custom database tables
- Single-file plugin architecture
- GPL-3.0 license
Requirements
- PHP 7.4+
- WordPress 6.0+
Installation
- Download the repository as a ZIP file.
- Open Plugins → Add New Plugin → Upload Plugin.
- Upload the ZIP file.
- Install and activate the plugin.
Default Behavior
The default limit is:
4 accepted searches per 60 seconds
Requests 1 through 4 are accepted.
Request 5 is blocked until the current 60-second window expires.
The limit applies independently to each detected IP address.
Cache Storage
When a persistent Object Cache is active, the plugin uses:
wp_cache_get()
wp_cache_set()
Otherwise, it uses the WordPress Transients API.
The plugin creates no custom database tables.
Trusted Proxies and CDNs
For security, forwarding headers such as X-Forwarded-For are ignored by default.
Without this protection, a visitor could submit a forged proxy header and bypass an IP-based rate limit.
When the website is behind a trusted reverse proxy, CDN or load balancer, explicitly register the proxy IP or CIDR range.
Example:
add_filter(
'wp_srl_trusted_proxy_ips',
static function () {
return array(
'127.0.0.1',
'10.0.0.0/8',
'192.168.1.10',
'2001:db8::/32',
);
}
);
Only when REMOTE_ADDR matches one of these values will the plugin inspect proxy headers.
Proxy Header Order
The default header order is:
HTTP_CF_CONNECTING_IP
HTTP_TRUE_CLIENT_IP
HTTP_X_REAL_IP
HTTP_X_FORWARDED_FOR
Change it with:
add_filter(
'wp_srl_proxy_header_order',
static function () {
return array(
'HTTP_CF_CONNECTING_IP',
'HTTP_X_FORWARDED_FOR',
);
}
);
Do not enable proxy headers unless the immediate proxy is trusted and removes user-supplied copies of those headers.
Filters
wp_srl_window_seconds
Changes the rate-limit window.
add_filter(
'wp_srl_window_seconds',
static function () {
return 120;
}
);
wp_srl_maximum_hits
Changes the number of accepted searches.
add_filter(
'wp_srl_maximum_hits',
static function () {
return 6;
}
);
With a value of 6, request 7 is blocked.
wp_srl_limit_empty_searches
Includes empty ?s= requests in the limit.
add_filter(
'wp_srl_limit_empty_searches',
'__return_true'
);
wp_srl_should_process_request
Conditionally disables rate limiting.
add_filter(
'wp_srl_should_process_request',
static function ( $should_process ) {
if ( is_user_logged_in() ) {
return false;
}
return $should_process;
}
);
wp_srl_trusted_proxy_ips
Defines trusted proxy IPs or CIDR ranges.
add_filter(
'wp_srl_trusted_proxy_ips',
static function () {
return array(
'10.0.0.0/8',
'172.16.0.0/12',
);
}
);
wp_srl_blocked_page_title
Changes the blocked-page title.
add_filter(
'wp_srl_blocked_page_title',
static function () {
return 'Search temporarily limited';
}
);
wp_srl_blocked_page_message
Changes the blocked-page message.
add_filter(
'wp_srl_blocked_page_message',
static function () {
return 'Please wait before submitting another search.';
}
);
Action Hook
wp_srl_search_blocked
Runs when a search request is blocked.
add_action(
'wp_srl_search_blocked',
static function ( $ip, $remaining, $count, $search_term ) {
error_log(
sprintf(
'Blocked search from %s for "%s". Retry in %d seconds.',
$ip,
$search_term,
$remaining
)
);
},
10,
4
);
Avoid storing raw IP addresses indefinitely unless your privacy policy and applicable regulations permit it.
Limitations
The limiter uses a fixed window rather than a sliding-window or token-bucket algorithm.
A user may submit requests near the end and beginning of two adjacent windows. For stronger abuse prevention on high-traffic websites, an edge-level rate limiter provided by a CDN, reverse proxy or web server is generally preferable.
This plugin protects the standard WordPress frontend ?s= search request. Custom AJAX search plugins, REST-based search endpoints, Elasticsearch integrations, and third-party search APIs require separate integration.
Privacy
The plugin hashes the IP address before using it as a cache key.
The raw IP address is not stored in the transient or Object Cache value.
WordPress and the hosting server may still process or log IP addresses independently.
Data Storage
The plugin temporarily stores:
- Window start timestamp
- Search request count
The cache key is derived from a SHA-256 hash of:
Site URL + IP address
Records expire automatically at the end of the active rate-limit window.
Security
The plugin includes:
- Direct-file-access protection
- Sanitized request values
- Validated IPv4 and IPv6 addresses
- Trusted proxy enforcement
- Exact IP and CIDR matching
- Hashed cache identifiers
- Escaped HTML output
- HTTP 429 responses
- Cache-prevention headers
- No direct SQL queries
- No external API requests
Changelog
1.0.0
- Initial release
- Added fixed-window search limiting
- Added configurable request limit and window
- Added HTTP 429 responses
- Added
Retry-Afterheader - Added countdown page
- Added persistent Object Cache support
- Added Transients fallback
- Added trusted proxy handling
- Added IPv4 and IPv6 CIDR matching
- Added filters and blocked-request action
License
GPL-3.0
Author
Amirreza Shayesteh Far
- Website: https://amirrezaa.ir/
- GitHub: https://github.com/amirrezashf
- Repository: https://github.com/amirrezashf/WordPress-Search-Rate-Limiter
محدودکننده جستجوی وردپرس
محدود کردن تعداد درخواستهای جستجوی frontend وردپرس بر اساس IP، همراه با پشتیبانی از proxyهای قابل اعتماد، cache و صفحه شمارش معکوس.
توضیحات
افزونه WordPress Search Rate Limiter از endpoint استاندارد جستجوی وردپرس در برابر درخواستهای پرتعداد و تکراری محافظت میکند.
بهصورت پیشفرض، هر IP میتواند در یک بازه ۶۰ ثانیهای حداکثر چهار جستجو انجام دهد. درخواست پنجم تا پایان بازه مسدود میشود.
پاسخ درخواست مسدودشده شامل موارد زیر است:
- وضعیت HTTP برابر
429 - هدر
Retry-After - هدرهای جلوگیری از cache
- صفحه واکنشگرا با شمارش معکوس
- تلاش خودکار پس از پایان محدودیت
این افزونه فقط روی درخواستهای frontend دارای پارامتر استاندارد ?s= اعمال میشود.
ویژگیها
- محدودیت جستجو بر اساس IP
- چهار جستجوی مجاز در ۶۰ ثانیه
- مسدود شدن درخواست پنجم
- پاسخ HTTP 429
- هدر Retry-After
- شمارش معکوس
- بارگذاری مجدد خودکار
- پشتیبانی از Persistent Object Cache
- fallback به Transients API
- پشتیبانی امن از proxy و CDN
- پشتیبانی از IPv4 و IPv6
- پشتیبانی از IP و CIDR
- عدم اعمال در پنل مدیریت
- عدم اعمال روی REST API
- عدم اعمال روی AJAX
- عدم اعمال روی XML-RPC
- عدم اعمال روی WP-CLI
- تنظیمپذیری محدودیت و متن پیام
- بدون جدول اختصاصی دیتابیس
- معماری تکفایلی
- مجوز GPL-3.0
نیازمندیها
- PHP 7.4+
- WordPress 6.0+
نصب
- repository را بهصورت ZIP دانلود کنید.
- وارد افزونهها ← افزودن افزونه تازه ← بارگذاری افزونه شوید.
- فایل ZIP را انتخاب کنید.
- افزونه را نصب و فعال کنید.
رفتار پیشفرض
محدودیت پیشفرض:
۴ جستجوی مجاز در هر ۶۰ ثانیه
درخواستهای اول تا چهارم پذیرفته میشوند.
درخواست پنجم تا پایان بازه مسدود میشود.
محدودیت برای هر IP بهصورت مستقل محاسبه میشود.
ذخیرهسازی Cache
اگر Persistent Object Cache فعال باشد، افزونه از توابع زیر استفاده میکند:
wp_cache_get()
wp_cache_set()
در غیر این صورت از Transients API استفاده میشود.
جدول اختصاصی دیتابیس ایجاد نمیشود.
Proxy و CDN قابل اعتماد
هدرهایی مانند X-Forwarded-For بهصورت پیشفرض نادیده گرفته میشوند.
اعتماد مستقیم به این هدر باعث میشود بازدیدکننده بتواند IP جعلی ارسال کرده و محدودیت را دور بزند.
در صورت استفاده از reverse proxy، CDN یا load balancer، IP یا محدوده CIDR آن را بهصورت صریح ثبت کنید:
add_filter(
'wp_srl_trusted_proxy_ips',
static function () {
return array(
'127.0.0.1',
'10.0.0.0/8',
'192.168.1.10',
'2001:db8::/32',
);
}
);
فقط زمانی که REMOTE_ADDR با یکی از این مقادیر مطابقت داشته باشد، هدرهای proxy بررسی میشوند.
ترتیب هدرهای Proxy
ترتیب پیشفرض:
HTTP_CF_CONNECTING_IP
HTTP_TRUE_CLIENT_IP
HTTP_X_REAL_IP
HTTP_X_FORWARDED_FOR
تغییر ترتیب:
add_filter(
'wp_srl_proxy_header_order',
static function () {
return array(
'HTTP_CF_CONNECTING_IP',
'HTTP_X_FORWARDED_FOR',
);
}
);
هدر proxy را فقط زمانی فعال کنید که proxy اصلی قابل اعتماد باشد و نسخه ارسالشده توسط کاربر را حذف یا بازنویسی کند.
فیلترها
wp_srl_window_seconds
تغییر طول بازه:
add_filter(
'wp_srl_window_seconds',
static function () {
return 120;
}
);
wp_srl_maximum_hits
تغییر تعداد جستجوهای مجاز:
add_filter(
'wp_srl_maximum_hits',
static function () {
return 6;
}
);
با مقدار 6، درخواست هفتم مسدود میشود.
wp_srl_limit_empty_searches
اعمال محدودیت روی ?s= خالی:
add_filter(
'wp_srl_limit_empty_searches',
'__return_true'
);
wp_srl_should_process_request
غیرفعال کردن شرطی محدودیت:
add_filter(
'wp_srl_should_process_request',
static function ( $should_process ) {
if ( is_user_logged_in() ) {
return false;
}
return $should_process;
}
);
wp_srl_trusted_proxy_ips
تعریف IP یا CIDRهای قابل اعتماد:
add_filter(
'wp_srl_trusted_proxy_ips',
static function () {
return array(
'10.0.0.0/8',
'172.16.0.0/12',
);
}
);
wp_srl_blocked_page_title
تغییر عنوان صفحه:
add_filter(
'wp_srl_blocked_page_title',
static function () {
return 'محدودیت موقت جستجو';
}
);
wp_srl_blocked_page_message
تغییر متن صفحه:
add_filter(
'wp_srl_blocked_page_message',
static function () {
return 'برای جستجوی دوباره کمی صبر کنید.';
}
);
Action Hook
wp_srl_search_blocked
هنگام مسدود شدن درخواست اجرا میشود:
add_action(
'wp_srl_search_blocked',
static function ( $ip, $remaining, $count, $search_term ) {
error_log(
sprintf(
'Blocked search from %s for "%s". Retry in %d seconds.',
$ip,
$search_term,
$remaining
)
);
},
10,
4
);
IP خام را فقط در صورتی برای مدت طولانی ذخیره کنید که سیاست حریم خصوصی و قوانین مربوطه اجازه دهند.
محدودیتها
الگوریتم افزونه fixed window است و sliding window یا token bucket نیست.
کاربر ممکن است در انتهای یک بازه و ابتدای بازه بعدی چند درخواست نزدیک به هم ارسال کند. برای وبسایتهای پرترافیک، rate limit در سطح CDN، reverse proxy یا web server معمولاً محافظت قویتری ایجاد میکند.
این افزونه فقط جستجوی استاندارد وردپرس با ?s= را پوشش میدهد. جستجوی AJAX، REST، Elasticsearch و سرویسهای جستجوی شخص ثالث به integration جداگانه نیاز دارند.
حریم خصوصی
IP پیش از استفاده بهعنوان کلید cache با SHA-256 hash میشود.
IP خام در مقدار transient یا Object Cache ذخیره نمیشود.
ممکن است خود وردپرس، سرور یا سرویس میزبانی IP را بهصورت مستقل پردازش یا log کند.
ذخیرهسازی داده
افزونه بهصورت موقت موارد زیر را ذخیره میکند:
- زمان شروع بازه
- تعداد درخواستهای جستجو
کلید cache از hash مقدار زیر ساخته میشود:
Site URL + IP address
رکوردها پس از پایان بازه بهصورت خودکار منقضی میشوند.
امنیت
- جلوگیری از دسترسی مستقیم
- پاکسازی ورودیها
- اعتبارسنجی IPv4 و IPv6
- اعتماد فقط به proxyهای تعریفشده
- پشتیبانی از CIDR
- hash کردن شناسه cache
- escape خروجی HTML
- پاسخ HTTP 429
- هدرهای جلوگیری از cache
- بدون SQL مستقیم
- بدون درخواست API خارجی
تغییرات نسخهها
1.0.0
- انتشار اولیه
- افزودن محدودیت fixed window
- تنظیم تعداد درخواست و بازه
- پاسخ HTTP 429
- هدر Retry-After
- صفحه شمارش معکوس
- پشتیبانی از Object Cache
- fallback به Transients API
- مدیریت امن proxy
- پشتیبانی IPv4 و IPv6 CIDR
- افزودن filterها و action مسدودسازی
مجوز
GPL-3.0
نویسنده
Amirreza Shayesteh Far
- وبسایت: https://amirrezaa.ir/
- GitHub: https://github.com/amirrezashf
- Repository: https://github.com/amirrezashf/WordPress-Search-Rate-Limiter