
Most Laravel applications eventually need some form of cooldown.
Not request throttling—but actual action cooldowns.
Examples include:
- Password reset requests
- Email verification
- OTP / SMS sending
- AI prompt generation
- Report exports
- Payment retries
- Reward claiming
- Expensive background jobs
Laravel already provides an excellent RateLimiter, but it's primarily designed for request throttling (for example, "60 requests per minute").
When I was building an application, I found myself needing something slightly different:
- Persist cooldowns in either cache or database
- Attach cooldowns directly to Eloquent models
- Reuse the same API across controllers, jobs, commands, and middleware
- Store cooldowns for arbitrary targets such as Users, API tokens, IP addresses, or custom identifiers
- Allow switching storage drivers without changing application code
That led me to build Laravel Cooldown.
The Goal
I wanted an API that was expressive enough to read almost like English.
Cooldown::for('password_reset', $user)->enforce();
// Perform the action...
Cooldown::for('password_reset', $user)->for(300);
Checking a cooldown is equally straightforward:
if (Cooldown::for('password_reset', $user)->active()) {
$remaining = Cooldown::for('password_reset', $user)
->info()
->remainingSeconds();
}
Some Design Decisions
Rather than coupling everything to a single storage implementation, the package uses Laravel's Illuminate\Support\Manager to provide interchangeable drivers.
Currently it supports:
- Cache
- Database
Additional drivers can be registered using Laravel's familiar extend() pattern.
Another design decision was to support Eloquent models as first-class cooldown targets.
Instead of manually generating cache keys, you can simply write:
$user->cooldown('email_verification')->for(300);
The package automatically resolves unique keys for models, scalars, IP addresses, and other supported targets.
Middleware That Only Starts on Success
One small behavior that bothered me with many cooldown implementations is that they start counting down before the request has actually succeeded.
Imagine submitting a form that fails validation.
You shouldn't have to wait another minute just because you mistyped your email address.
The middleware in Laravel Cooldown only creates the cooldown after successful (2xx) or redirected (3xx) responses.
Features
- Cache and Database drivers
- Expressive fluent API
- Native Eloquent integration
- Route middleware
- Immutable DTOs
- Event dispatching
- Automatic database pruning
- Custom driver support
Feedback Welcome
This is an open-source project, and I'd really appreciate feedback from the Laravel community.
If you have suggestions for the API, architecture, or documentation—or ideas for additional drivers or features—I'd love to hear them.
Top comments (0)