Getting started
IMPORTANT
Public beta. Everything planned for 0.1.0 is in, but names and options may still change, and the changelog will say how to upgrade. Try it in staging first, and tell us what breaks or confuses you.
Requirements
- PHP 8.3+, and Laravel 12.69+ or 13.30+.
- A counter store: Redis 6.2+ or Valkey 7.2+ is best, and the live feed and offenders need one. Memcached and the database store work too, but not
fileorapcin production. - On PHP-FPM, the APCu extension, so a server's workers share the rules.
Installation
1. Install the beta:
composer require hanzala-afzal/laravel-dynamic-throttle:^0.1@beta2. Create the tables, and compile the rules once. After this, saving a rule compiles them, and so does the scheduler.
php artisan vendor:publish --tag=dynamic-throttle-migrations
php artisan migrate
php artisan dynamic-throttle:compile3. Attach the middleware to the routes rules may limit, in bootstrap/app.php. Routes without it are never limited.
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('api', 'dynamic.throttle');
})4. Say who may use the dashboard, in a service provider's boot(). Until you do, only the local environment gets in, and php artisan dynamic-throttle:health warns everywhere else.
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::define('viewDynamicThrottle', fn (User $user) => $user->is_admin);
Gate::define('manageDynamicThrottle', fn (User $user) => $user->is_admin);5. Run Laravel's scheduler. It recompiles the rules every five minutes and stores the traffic numbers every minute.
Then check your setup with php artisan dynamic-throttle:health.
Usage
Add a rule in the dashboard under Rules → New rule, from a template such as "Login" or "API per user". New rules start in dry-run: they count requests and show who they would block, but block no one until you promote them.
Or from code:
use Afzal\DynamicThrottle\Models\Rule;
Rule::create([
'name' => 'API per user',
'pattern' => 'api/*',
'scope' => 'user', // ip, user, api_token, header, global or custom
'max_attempts' => 60,
'window_seconds' => 60,
]);Each user now gets 60 requests a minute under /api. The 61st gets Laravel's own 429, with Retry-After and X-RateLimit-* headers.
| Command | What it does |
|---|---|
dynamic-throttle:health | Looks for setup mistakes. --strict fails on a warning too |
dynamic-throttle:explain GET /api/orders --user=42 | Shows why a request would be allowed or limited, without counting it |
dynamic-throttle:status | Shows what the servers apply now, and until when |
dynamic-throttle:bypass on --for=30m | Lifts every limit until the end time |
dynamic-throttle:scale 2 --for=2h | Multiplies every limit: 2 doubles them, 0.5 halves them |
dynamic-throttle:disable-rule 12, enable-rule 12 | Switches one rule off or back on |
dynamic-throttle:list | Lists every rule with its id |
dynamic-throttle:compile | Recompiles the rules now |
Beta feedback
What helps most:
- Did it install cleanly on your Laravel and PHP versions, cache store and runtime (PHP-FPM, Octane, Vapor)?
- Did a rule do something you did not expect? Paste the output of
php artisan dynamic-throttle:explain. - Was anything in the dashboard or the docs unclear?
Report bugs in issues, and bring questions and ideas to discussions. Report security issues privately, as SECURITY.md explains, never in a public issue.