From 00dceea3d8895b9c46d8d53ffe5d87283c355a1c Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 15 Aug 2025 15:16:30 +0200 Subject: [PATCH] Add RateLimit middleware docs. --- en/appendices/5-3-migration-guide.rst | 9 + en/controllers/middleware.rst | 3 + en/controllers/middleware/rate-limit.rst | 251 +++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 en/controllers/middleware/rate-limit.rst diff --git a/en/appendices/5-3-migration-guide.rst b/en/appendices/5-3-migration-guide.rst index b2ede3140a..ac44d21414 100644 --- a/en/appendices/5-3-migration-guide.rst +++ b/en/appendices/5-3-migration-guide.rst @@ -64,6 +64,15 @@ Command - ``cake server`` now supports a ``--frankenphp`` option that will start the development server with `FrankenPHP `__. +Http +---- + +- The new ``RateLimitMiddleware`` provides configurable rate limiting for your + application to protect against abuse and ensure fair usage of resources. It + supports multiple identification strategies (IP, user, route, API key), + different rate limiting algorithms (sliding window, fixed window, token bucket), + and advanced features like custom identifiers, request costs, and dynamic limits. + Cache ----- diff --git a/en/controllers/middleware.rst b/en/controllers/middleware.rst index 9a3a1365f5..a78a4bd157 100644 --- a/en/controllers/middleware.rst +++ b/en/controllers/middleware.rst @@ -57,6 +57,9 @@ CakePHP provides several middleware to handle common tasks in web applications: * :doc:`Cake\Http\Middleware\SecurityHeadersMiddleware ` makes it possible to add security related headers like ``X-Frame-Options`` to responses. +* :doc:`Cake\Http\Middleware\RateLimitMiddleware ` + provides configurable rate limiting to protect against abuse and ensure fair + usage of resources. .. _using-middleware: diff --git a/en/controllers/middleware/rate-limit.rst b/en/controllers/middleware/rate-limit.rst new file mode 100644 index 0000000000..1b715a99df --- /dev/null +++ b/en/controllers/middleware/rate-limit.rst @@ -0,0 +1,251 @@ +Rate Limiting Middleware +######################## + +.. versionadded:: 5.3 + +The ``RateLimitMiddleware`` provides configurable rate limiting for your +application to protect against abuse and ensure fair usage of resources. + +Basic Usage +=========== + +To use rate limiting in your application, add the middleware to your +middleware queue:: + + // In src/Application.php + use Cake\Http\Middleware\RateLimitMiddleware; + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $middlewareQueue + // ... other middleware + ->add(new RateLimitMiddleware([ + 'limit' => 60, // 60 requests + 'window' => 60, // per 60 seconds + 'identifier' => 'ip', // based on IP address + ])); + + return $middlewareQueue; + } + +When a client exceeds the rate limit, they will receive a +``429 Too Many Requests`` response. + +Configuration Options +===================== + +The middleware accepts the following configuration options: + +- **limit** - Maximum number of requests allowed (default: 60) +- **window** - Time window in seconds (default: 60) +- **identifier** - How to identify clients: 'ip', 'user', 'route', 'api_key' (default: 'ip') +- **strategy** - Rate limiting algorithm: 'sliding_window', 'fixed_window', 'token_bucket' (default: 'sliding_window') +- **cache** - Cache configuration to use (default: 'default') +- **headers** - Whether to include rate limit headers in responses (default: true) +- **message** - Custom error message for rate limit exceeded +- **skipCheck** - Callback to determine if a request should skip rate limiting +- **costCallback** - Callback to determine the cost of a request +- **identifierCallback** - Callback to determine the identifier for a request +- **limitCallback** - Callback to determine the limit for a specific identifier + +Identifier Types +================ + +IP Address +---------- + +The default identifier type tracks requests by IP address:: + + new RateLimitMiddleware([ + 'identifier' => 'ip', + 'limit' => 100, + 'window' => 60, + ]) + +The middleware automatically handles proxy headers like ``X-Forwarded-For`` +and ``CF-Connecting-IP``. + +User-based +---------- + +Track requests per authenticated user:: + + new RateLimitMiddleware([ + 'identifier' => 'user', + 'limit' => 1000, + 'window' => 3600, // 1 hour + ]) + +This requires authentication middleware to be loaded before rate limiting. + +Route-based +----------- + +Apply different limits to different routes:: + + new RateLimitMiddleware([ + 'identifier' => 'route', + 'limit' => 10, + 'window' => 60, + ]) + +This creates separate limits for each controller/action combination. + +API Key +------- + +Track requests by API key:: + + new RateLimitMiddleware([ + 'identifier' => 'api_key', + 'limit' => 5000, + 'window' => 3600, + ]) + +The middleware looks for API keys in the ``X-API-Key`` header or +``api_key`` query parameter. + +Custom Identifiers +================== + +You can create custom identifiers using a callback:: + + new RateLimitMiddleware([ + 'identifierCallback' => function ($request) { + // Custom logic to identify the client + $tenant = $request->getHeader('X-Tenant-ID'); + return 'tenant_' . $tenant[0]; + }, + ]) + +Rate Limiting Strategies +======================== + +Sliding Window +-------------- + +The default strategy that provides smooth rate limiting by continuously +adjusting the window based on request timing:: + + new RateLimitMiddleware([ + 'strategy' => 'sliding_window', + ]) + +Fixed Window +------------ + +Resets the counter at fixed intervals:: + + new RateLimitMiddleware([ + 'strategy' => 'fixed_window', + ]) + +Token Bucket +------------ + +Allows for burst capacity while maintaining an average rate:: + + new RateLimitMiddleware([ + 'strategy' => 'token_bucket', + 'limit' => 100, // bucket capacity + 'window' => 60, // refill rate + ]) + +Advanced Usage +============== + +Skip Rate Limiting +------------------ + +Skip rate limiting for certain requests:: + + new RateLimitMiddleware([ + 'skipCheck' => function ($request) { + // Skip rate limiting for health checks + return $request->getParam('action') === 'health'; + }, + ]) + +Request Cost +------------ + +Assign different costs to different types of requests:: + + new RateLimitMiddleware([ + 'costCallback' => function ($request) { + // POST requests cost 5x more + return $request->getMethod() === 'POST' ? 5 : 1; + }, + ]) + +Dynamic Limits +-------------- + +Set different limits for different users or plans:: + + new RateLimitMiddleware([ + 'limitCallback' => function ($request, $identifier) { + $user = $request->getAttribute('identity'); + if ($user && $user->plan === 'premium') { + return 10000; // Premium users get higher limit + } + return 100; // Free tier limit + }, + ]) + +Rate Limit Headers +================== + +When enabled, the middleware adds the following headers to responses: + +- ``X-RateLimit-Limit`` - The maximum number of requests allowed +- ``X-RateLimit-Remaining`` - The number of requests remaining +- ``X-RateLimit-Reset`` - Unix timestamp when the rate limit resets +- ``X-RateLimit-Reset-Date`` - ISO 8601 date when the rate limit resets + +Multiple Rate Limiters +====================== + +You can apply multiple rate limiters with different configurations:: + + // Strict limit for login attempts + $middlewareQueue->add(new RateLimitMiddleware([ + 'identifier' => 'ip', + 'limit' => 5, + 'window' => 900, // 15 minutes + 'skipCheck' => function ($request) { + return $request->getParam('action') !== 'login'; + }, + ])); + + // General API rate limit + $middlewareQueue->add(new RateLimitMiddleware([ + 'identifier' => 'api_key', + 'limit' => 1000, + 'window' => 3600, + ])); + +Cache Configuration +=================== + +The rate limiter stores its data in cache. Make sure you have a persistent +cache configured:: + + // In config/app.php + 'Cache' => [ + 'rate_limit' => [ + 'className' => 'Redis', + 'prefix' => 'rate_limit_', + 'duration' => '+1 hour', + ], + ], + +Then use it in the middleware:: + + new RateLimitMiddleware([ + 'cache' => 'rate_limit', + ]) + +.. warning:: + The ``File`` cache engine is not recommended for production use with + rate limiting as it may not handle concurrent requests properly. \ No newline at end of file