Skip to content
Pro Analyzer — Available with ShieldCI Pro

Redis Throttling Analyzer ​

Analyzer IDCategorySeverityTime To Fix
redis-throttling⚡ PerformanceLow10 minutes

What This Checks ​

When your application uses Redis, this analyzer suggests using ThrottleRequestsWithRedis instead of the standard ThrottleRequests middleware for more accurate rate limiting under high concurrency. Checks for:

  • ThrottleRequests middleware registered in global middleware when Redis is available
  • The throttle alias mapped to ThrottleRequests instead of ThrottleRequestsWithRedis
  • Routes using the non-Redis throttle middleware

Why It Matters ​

  • Atomic Operations: Redis-based throttling uses atomic Lua scripts for rate limiting
  • Race Condition Prevention: Standard throttling can allow bursts due to cache read/write race conditions
  • High Concurrency: More accurate under heavy load when multiple requests arrive simultaneously
  • Distributed Systems: Works correctly across multiple application servers

The standard ThrottleRequests middleware reads the current count, checks if the limit is exceeded, then increments — three separate cache operations. Under high concurrency, multiple requests can read the same count before any increment occurs, allowing brief bursts past the configured limit.

How to Fix ​

Quick Fix ​

php
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->throttleWithRedis();
    // ...
})
php
// app/Http/Kernel.php
protected $middlewareAliases = [
    // Change this:
    // 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,

    // To this:
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,

    // ... other aliases
];

throttleWithRedis() remaps the throttle alias to ThrottleRequestsWithRedis. All routes using throttle:60,1 will automatically use atomic rate limiting with no other changes needed.

Alias override pitfall

On Laravel 11+, calling $middleware->throttleWithRedis() and then passing 'throttle' => ThrottleRequests::class to $middleware->alias() in the same withMiddleware() block will override the Redis mapping. Remove the explicit throttle entry from alias() when using throttleWithRedis().

Conditional Redis throttling ​

If your application uses Redis only in some environments (for example, Redis in production but an array or database store locally and in tests), swap the throttle alias at runtime in a service provider rather than hard-coding throttleWithRedis():

php
// app/Providers/AppServiceProvider.php
use Illuminate\Routing\Middleware\ThrottleRequestsWithRedis;
use Illuminate\Support\Facades\Route;

public function boot(): void
{
    if (config('cache.default') === 'redis') {
        Route::aliasMiddleware('throttle', ThrottleRequestsWithRedis::class);
    }
}

ShieldCI recognizes this runtime swap as a valid fix, so routes using throttle:60,1 are reported as compliant.

Don't gate Redis throttling on env()

Wrapping $middleware->throttleWithRedis() in an env('CACHE_STORE') === 'redis' check is unreliable in production. Once php artisan config:cache runs, the .env file is no longer read, so env() falls back to its default and the Redis throttler is silently never applied. Read config('cache.default') from a service provider's boot() instead, where cached config is always available.

Explicit per-route middleware ​

If you need different throttle behaviour per route group without changing the global alias, apply ThrottleRequestsWithRedis directly:

php
use Illuminate\Routing\Middleware\ThrottleRequestsWithRedis;

Route::middleware([ThrottleRequestsWithRedis::class.':60,1'])->group(function () {
    Route::get('/users', [UserController::class, 'index']);
});

Prerequisites ​

ThrottleRequestsWithRedis needs a configured Redis connection (the redis block in config/database.php). In most applications this goes hand in hand with using Redis as the cache store:

ini
# .env
CACHE_STORE=redis
ini
# .env
CACHE_DRIVER=redis

References ​