← All projects

Laravel Rate Limit Dashboard

Laravel can define rate limits but shows you nothing about who hits them. This package adds instrumentation, persisted metrics, a secured dashboard, and runtime limiter controls so abuse and misconfiguration are visible before they become support tickets.

Role
Author & Maintainer
Period
Open source
Team
Independent build
Stack
PHP · Laravel · Blade · Composer · Packagist

Problem

Laravel’s RateLimiter facade and throttle middleware let you define limits, but they give you no visibility into what those limits are doing in production. When a user hits an HTTP 429 Too Many Requests, there is no built-in way to answer the questions that actually matter during an incident:

  • Which IP, user, or API token is being throttled?
  • Which endpoint is being hammered?
  • Can we raise or lower a limit without shipping a deploy?

Without answers, throttling becomes a black box. Abuse goes unchecked because nobody can see it. Limits get misconfigured because there is no feedback loop. Legitimate users get blocked and the only signal is a support ticket. The data needed to reason about rate limiting exists only as ephemeral counters in the cache — it is never persisted, aggregated, or surfaced.

Constraints

  • Use the host’s own infrastructure. No new datastore. Metrics persist through Eloquent to the application’s configured SQL database.
  • Drop-in over Laravel’s own primitives. It must build on the framework’s RateLimiter, not replace it. Teams should adopt it by swapping middleware, not rearchitecting throttling.
  • Secured by default. The dashboard exposes who is hitting your API. It must sit behind a gate or middleware the host controls — never world-readable.
  • Low overhead on the hot path. Instrumentation runs on every throttled request. Raw event capture must be cheap; aggregation and pruning happen out of band.
  • Operable, not just observable. Beyond charts, it must allow runtime limiter configuration and per-user / per-IP overrides without a redeploy.
  • CI- and ops-friendly. Health checks, threshold alerts, and retention cleanup must be reachable from php artisan.

Key decisions

Instrumenter middleware as the capture point. A dedicated RateLimitInstrumenter middleware wraps the limiter for routes you opt in. It records the request, the resolved limiter, and the outcome, then defers to Laravel’s normal throttling behaviour. Adoption is a one-line middleware swap per route; routes you do not instrument are untouched.

Route::middleware(\Sa\RateLimitDashboard\Http\Middleware\RateLimitInstrumenter::class.':api')
    ->get('/api/search', SearchController::class);

Persist to the host database via Eloquent. Raw events, runtime limiter configuration, audit entries, and rolled-up minute/hour/day summaries all live in the application’s configured SQL database. No Redis-only counters that vanish, no separate metrics store to operate. Summaries make the dashboard queries cheap regardless of raw event volume.

Secured dashboard behind a host-controlled gate. The dashboard route (default /admin/rate-limits) renders only when the viewRateLimitDashboard gate passes, or behind the host’s own middleware. Exposing offender IPs and tokens without authorization would be a vulnerability, so protection is required, not optional — and a health check flags it if left open.

Runtime configuration and overrides. Package-managed limiter settings are editable from the UI and persisted, and per-user / per-IP overrides apply when the instrumenter middleware is in use. Tightening a limit on an abusive token, or loosening one for a trusted partner, no longer requires a code change and deploy.

Health checks as a first-class surface. Built-in checks cover storage, dashboard protection, utilisation, decay settings, offenders, and unconfigured routes. They turn “is rate limiting set up correctly?” from tribal knowledge into a concrete pass/fail list.

Alerts and retention as scheduled commands. rate-limit:check-alerts sends mail when configured thresholds are breached; rate-limit:prune trims old raw events so the events table does not grow unbounded. Aggregated summaries are retained; raw rows are disposable.

Example usage

# Install
composer require satheez/laravel-rate-limit-dashboard

# Publish config and migrations
php artisan vendor:publish --provider="Sa\RateLimitDashboard\RateLimitDashboardServiceProvider" --tag="rate-limit-dashboard-config"
php artisan vendor:publish --provider="Sa\RateLimitDashboard\RateLimitDashboardServiceProvider" --tag="rate-limit-dashboard-migrations"
php artisan migrate

# Mail alerts when a configured threshold is breached
php artisan rate-limit:check-alerts

# Trim old raw events (schedule this)
php artisan rate-limit:prune

Instrument a route and gate the dashboard:

// routes/api.php
Route::middleware(\Sa\RateLimitDashboard\Http\Middleware\RateLimitInstrumenter::class.':api')
    ->get('/api/search', SearchController::class);

// AuthServiceProvider
Gate::define('viewRateLimitDashboard', fn ($user) => $user->isAdmin());

Then visit /admin/rate-limits for total vs throttled requests, utilisation, hourly volume, recent events, limiter activity, and top offenders grouped by IP, user, or token through the JSON API.

Outcome

Rate limiting stops being a black box. Operators can see, in real time, who is being throttled and which endpoints are under pressure, then adjust limits or apply overrides without a deploy. Metrics persist in the app’s own database, so trends survive cache flushes. Health checks make misconfiguration — including an unprotected dashboard — visible, threshold alerts surface abuse proactively, and retention pruning keeps storage bounded. The questions that used to require log spelunking during an incident are answered on a page.

What I’d do differently

Persisting raw events to the primary SQL database is the right default for adoption — no new infrastructure — but high-traffic APIs will want a write path that does not touch the request hot loop synchronously. If I were extending it, I would add an optional queued or buffered ingestion mode and a pluggable storage driver so teams already running ClickHouse or a time-series store could route events there while keeping the same dashboard and checks.