php
47 lines · 6 steps
Throttling login attempts in Laravel
A service class uses Laravel's RateLimiter to block brute-force logins per email and IP.
Explained by
highlit
1<?php
2
3namespace App\Services;
4
5use App\Models\User;
6use Illuminate\Support\Facades\Cache;
7use Illuminate\Support\Facades\RateLimiter;
8use Illuminate\Validation\ValidationException;
9
10class LoginThrottle
11{
12 private const MAX_ATTEMPTS = 5;
13 private const DECAY_SECONDS = 900;
14
15 public function ensureNotLocked(string $email, string $ip): void
16 {
17 $key = $this->throttleKey($email, $ip);
18
19 if (! RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) {
20 return;
21 }
22
23 $seconds = RateLimiter::availableIn($key);
24
25 throw ValidationException::withMessages([
26 'email' => trans('auth.throttle', [
27 'seconds' => $seconds,
28 'minutes' => ceil($seconds / 60),
29 ]),
30 ])->status(429);
31 }
32
33 public function recordFailure(string $email, string $ip): void
34 {
35 RateLimiter::hit($this->throttleKey($email, $ip), self::DECAY_SECONDS);
36 }
37
38 public function clear(string $email, string $ip): void
39 {
40 RateLimiter::clear($this->throttleKey($email, $ip));
41 }
42
43 private function throttleKey(string $email, string $ip): string
44 {
45 return 'login:' . sha1(mb_strtolower($email) . '|' . $ip);
46 }
47}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Keying a throttle by both email and IP limits attackers without punishing every user sharing an address.
- 2Laravel's RateLimiter facade gives you attempt counting, decay, and remaining-time helpers for free.
- 3Returning HTTP 429 with a countdown message tells clients precisely when to retry.
Related explainers
php
<?php class NameParser {
Parsing a full name into components in PHP
string-parsing
arrays
normalization
Intermediate
8 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
php
<?php namespace App\Services;
Building a cached daily leaderboard in Laravel
caching
aggregation
eager-loading
Intermediate
9 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/throttling-login-attempts-in-laravel-explained-php-c477/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.