Imagine this — your app is running smoothly, then suddenly it gets hit with a wave of requests. Some from real users, others from bots...
Imagine this — your app is running smoothly, then suddenly it gets hit with a wave of requests. Some from real users, others from bots or people refreshing nonstop. Without proper rate limiting, your server could slow to a crawl or even go down.
Laravel’s built-in rate limiting helps prevent that. Think of it like a check-in line at a theme park — without regulation, it’s chaos. With a system in place, everything flows.
In this article, I’ll cover:
Rate limiting controls how many times someone can access a route or API in a certain time frame. It's like setting a speed limit for users or bots. Laravel’s rate limiting protects your app from rowdy bots and abusive users. 😉
Controlled entry = smooth experience 😄
To prevent abuse (e.g., brute-force login attempts), Laravel lets you define custom rate limiters using RateLimiter::for()
. In this example, we create a login limiter that allows 5 requests per minute per IP.
// Path: App\Providers\AppServiceProvider.php
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
public function boot(): void
{
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)
->by($request->ip())
->response(function () {
return response()->json([
'message' => 'Too many attempts. Try again later.'
], 429);
});
});
}
Apply the limiter to your route using throttle:{name}
middleware:
// Path: routes/web.php or routes/api.php
Route::middleware('throttle:login')->post('/login', [AuthController::class, 'login']);
👍 Best for most apps. Use Laravel’s built-in RateLimiter::for()
for simple, clean throttling. Want more control? Let’s explore custom strategies. 🚀
Different users deserve different access. Free users may get 60 requests/min, while premium users get 200/min.
Example: SaaS app with tiered plans. Give more access to paying customers, without hurting free-tier users.
// Path: App\Http\Middleware\RateLimitByRole.php
public function handle(Request $request, Closure $next){
$role = $request->user()?->role ?? 'guest';
$limits = ['admin' => 100, 'premium' => 200, 'free' => 60, 'guest' => 20];
$maxAttempts = $limits[$role] ?? 20;
$key = "rate:{$role}:" . ($request->user()?->id ?? $request->ip());
if (RateLimiter::tooManyAttempts($key, $maxAttempts)) {
return response()->json(['message' => 'Too many requests.'], 429);
}
RateLimiter::hit($key, 60);
return $next($request);
}
Useful for apps with tiered user roles (e.g., admin, staff, guest).
Limit requests based on IP to block specific bots or noisy users.
Example: During a sale, one IP spams checkout API. Block it without hurting others.
// Path: App\Http\Middleware\RateLimitByIP.php
public function handle(Request $request, Closure $next)
{
$ip = $request->ip();
$key = 'rate_limit:ip:' . $ip;
if (RateLimiter::tooManyAttempts($key, 60)) {
return response()->json(['message' => "You're doing that too much."], 429);
}
RateLimiter::hit($key, 60);
return $next($request);
}
Great for anonymous endpoints or rate-limiting crawlers.
When traffic surges, don’t crash — degrade. Politely pause extra requests, or give fallback data.
Example: Flash sale causes a traffic spike. Rather than failing, the app slows down politely.
// Path: App\Http\Middleware\BurstLimit.php
public function handle(Request $request, Closure $next)
{
$key = 'burst:' . $request->ip();
if (RateLimiter::tooManyAttempts($key, 30)) {
return response()->json(['message' => 'Whoa there! Slow down.'], 429);
}
RateLimiter::hit($key, 30);
return $next($request);
}
😎 Perfect when you want flexibility with protection.
Limit users based on their API token tier (free/paid/unknown).
Example: A weather API that limits based on token type.
// Path: App\Http\Middleware\RateLimitByToken.php
public function handle(Request $request, Closure $next)
{
$token = $request->bearerToken();
$key = 'rate_limit:token:' . ($token ?? $request->ip());
if (RateLimiter::tooManyAttempts($key, 100)) {
return response()->json(['message' => 'Rate limit exceeded'], 429);
}
RateLimiter::hit($key, 60);
return $next($request);
}
Ideal for public APIs with developer access.
bootstrap/app.php
use App\Http\Middleware\RateLimitByRole;
use App\Http\Middleware\RateLimitByIP;
use App\Http\Middleware\BurstLimit;
use App\Http\Middleware\RateLimitByToken;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
)
->withMiddleware(function ($middleware) {
$middleware->alias([
'role.throttle' => RateLimitByRole::class,
'ip.throttle' => RateLimitByIP::class,
'burst.throttle' => BurstLimit::class,
'token.throttle' => RateLimitByToken::class,
]);
})
->create();
Strategy | Keyed By | Example Limit |
---|---|---|
Role-Based | role:ip |
Admin: 100/min, User: 10/min |
IP-Based | ip |
60/min |
Token-Based | api_token |
100/min |
Graceful Bursting | ip |
30/min (short bursts allowed) |
What is rate limiting? It’s your app’s speed governor.
It isn’t just about blocking — it’s about protecting performance while being fair to your users. It improves the experience for everyone. Implement it early, tweak it as you grow and keep things running smooth.
RateLimiter::for()
for simple setups.Start small, scale smart — Happy coding! 😄
Subscribe to our "Article Digest". We'll send you a list of the new articles, every week, month or quarter - your choice.
What do you think about this?
Wondering what our community has been up to?