WP Manifestindependent plugin directory
manifest / security / wordpress-search-rate-limiter

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

0stars
0forks

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.zip

Readme

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-After response 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 Requests response
  • Retry-After response 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

  1. Download the repository as a ZIP file.
  2. Open Plugins → Add New Plugin → Upload Plugin.
  3. Upload the ZIP file.
  4. 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-After header
  • 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


محدودکننده جستجوی وردپرس

محدود کردن تعداد درخواست‌های جستجوی 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+

نصب

  1. repository را به‌صورت ZIP دانلود کنید.
  2. وارد افزونه‌ها ← افزودن افزونه تازه ← بارگذاری افزونه شوید.
  3. فایل ZIP را انتخاب کنید.
  4. افزونه را نصب و فعال کنید.

رفتار پیش‌فرض

محدودیت پیش‌فرض:

۴ جستجوی مجاز در هر ۶۰ ثانیه

درخواست‌های اول تا چهارم پذیرفته می‌شوند.

درخواست پنجم تا پایان بازه مسدود می‌شود.

محدودیت برای هر 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

Read the full README on GitHub →