rate-limiting
Skill4 starsChanged 4 months agoMITView on GitHub
Throttle requests and jobs in Laravel — define named limiters with the RateLimiter facade, apply the throttle middleware per route and per user, back limits with Redis for accuracy at scale, return correct 429 responses, and set dynamic per-plan limits. Use when the user needs per-user, per-IP, or dynamic per-plan rate limits defined with the RateLimiter facade; the user is choosing a rate-limit store (Redis vs the default cache) or debugging 429 responses and Retry-After headers; the user wants to throttle requests to a route or API with the throttle middleware or a named limiter. Keywords: rate-limiting, throttle, ratelimiter, middleware, redis, cache, performance, named-limiters, 429, api, input, laravel.
12 other projects use a version of this.
Adopted by
Most recently taken up by
- mahumadad/quarrytheir own version, first seen Nov 8, 2026
- d4rken/clankermuxtheir own version, first seen Nov 2, 2026
- jars-demo/jars-skillstheir own version, first seen Oct 25, 2026
- gerard-labs/superpowers-api-platformtheir own version, first seen Oct 13, 2026
- chunkydotdev/email-skillstheir own version, first seen Sep 29, 2026
- MANVENDRA-github/agentrytheir own version, first seen Jul 6, 2026
Used with
Projects with it also use background-jobs, input-validation and 6 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
YepAPISkillbackground-jobs
Queue workers with BullMQ/Inngest/Trigger.dev, job retries, dead letter queues, concurrency.
Also in 3 projects that use this
hereshecodesSkillinput-validation
Use when processing user input, form data, file uploads, or query parameters
Also in 3 projects that use this
wpankSkillcaching
Caching strategies, invalidation, eviction policies, HTTP caching, distributed caching, and anti-patterns. Use when designing cache layers, choosing eviction policies, debugging stale data, or optimizing read-heavy workloads.
Also in 3 projects that use this
SentrySkillfeature-flags
Gate a Sentry feature behind a FlagPole feature flag. Use when adding a feature flag, registering a flag in temporary.py, checking a flag from Python or the frontend, enabling a flag in tests, or asking where FlagPole rollout config lives. Trigger on "add a feature flag", "gate this behind a flag", "register a flag", "features.has", "api_expose", "OrganizationFeature", "ProjectFeature", "FlagPole".
Also in 3 projects that use this
xfstudioSkillsecrets-management
Implement secure secrets management for CI/CD pipelines using Vault, AWS Secrets Manager, or native platform solutions. Use when handling sensitive credentials, rotating secrets, or securing CI/CD environments.
Also in 3 projects that use this
OpenAISkillobservability
Vercel Observability expert guidance — Drains (logs, traces, speed insights, web analytics), Web Analytics, Speed Insights, runtime logs, custom events, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.
Also in 3 projects that use this
Other versions12 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 12
- harperaa/secure-claude-skills★ 16same name
- d4rken/clankermux★ 11same name
- wpank/ai★ 11same name
- YepAPI/skills★ 8same name
- chunkydotdev/email-skills★ 7same name
- ffsshhttiikk/opencode-agents-skills★ 2same name
- gerard-labs/superpowers-api-platform★ 2same name
- kimtth/agent-skill-100-lines-or-less★ 2same name
- mahumadad/quarry★ 1same name
- jars-demo/jars-skills★ 0same name
2 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository119 other files it gives its agents.
- strategy-bitrix-d7-incrementalSkill
- strategy-explicit-over-magicSkill
- strategy-framework-conventionsSkill
- strategy-legacy-safe-editsSkill
- strategy-minimal-diffSkill
- strategy-no-untested-refactorSkill
- strategy-preserve-public-apiSkill
- strategy-readonly-immutabilitySkill
- strategy-security-firstSkill
- strategy-test-drivenSkill
- openapiSkill
- paginationSkill
- rest-designSkill
- versioningSkill
- cqrsSkill
- dependency-inversionSkill
- hexagonalSkill
- repository-patternSkill
- service-layerSkill
- bitrixSkill
- findings-reportSkill
- laravelSkill
- performanceSkill
- phpSkill
- project-analysisSkill
- refactoringSkill
- roi-prioritizationSkill
- securitySkill
- stack-detectionSkill
- cache-layersSkill
- component-engineSkill
- d7-eventsSkill
- d7-loaderSkill
- d7-orm-queriesSkill
- iblock-elementsSkill
- kernel-main-moduleSkill
- localizationSkill
- controllersSkill
- ormSkill
- validationSkill
- controllersSkill
- query-builderSkill
- routingSkill
- autoload-psr4Skill
- dependency-managementSkill
- scriptsSkill
- connection-poolingSkill
- mysql-indexingSkill
- redisSkill
- transactionsSkill
- ci-github-actionsSkill
- deployment-strategiesSkill
- docker-phpSkill
- env-configSkill
- associationsSkill
- entities-mappingSkill
- orm-basicsSkill
- entity-apiSkill
- module-developmentSkill
- routing-controllersSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
9 rules. 0 are common, 6 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 10 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 77
Do not use rate limiting to authenticate or authorize callers.
- Distinctive: line 78
Do not use HTTP throttle middleware to pace queued jobs against a rate-limited API.
- Distinctive: line 131
Verify: The limiter name resolves and keys by user id with an IP fallback.
- Very specific: line 134
Verify: php artisan route:list shows throttle:api on the group and a burst returns 429.
- Distinctive: line 137
Verify: Exceeding the limit returns JSON 429 with Retry-After and X-RateLimit-* headers.
- Very specific: line 153
Verify: config('cache.default') is redis and counters appear as keys in Redis.
- Distinctive: line 156
Verify: The limit trips at the same total across both servers, not once per node.
- Very specific: line 157
Use a separate cache prefix or Redis database for limiter keys, and configure trusted proxies so request()->ip() is the real client behind …
- Distinctive: line 159
Verify: Limiter keys do not collide with app cache and IP-keyed limits use the real client IP.
“Common” is measured: kept by at least 6 in 10 of the 10 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- generated: do not edit — source: knowledge/atoms/laravel/rate-limiting/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->Laravel Rate Limiting
When to use
- The user needs per-user, per-IP, or dynamic per-plan rate limits defined with the RateLimiter facade.
- The user is choosing a rate-limit store (Redis vs the default cache) or debugging 429 responses and Retry-After headers.
- The user wants to throttle requests to a route or API with the throttle middleware or a named limiter.
When NOT to use
- Do not use rate limiting to authenticate or authorize callers; use auth guards and policies for identity, and rate limiting only to cap request volume.
- Do not use HTTP throttle middleware to pace queued jobs against a rate-limited API; use queue rate-limiting middleware such as RateLimited on the job instead.
Core guidance
- Key limiters by user id, falling back to IP — In
RateLimiter::for, key the limit by the authenticated user id and fall back to the request IP for guests ($request->user()?->id ?: $request->ip()). - Define named limiters instead of inline throttle numbers — Register named limiters with
RateLimiter::for('api', ...)and applythrottle:api, rather than scatteringthrottle:60,1literals across routes. - Back rate limits with Redis across multiple servers — When the app runs on more than one web server, set the cache store used by the
limiter to Redis so counters are shared, not the per-node
fileorarraystore.
Decision Engine
Walk this section before choosing an implementation path.
Where does the app run — many servers, a single server, or a non-HTTP hot path —
and how strict must the shared count be?
├── Single server or an already-shared cache store: → default cache store — the RateLimiter uses the app's configured cache; fine on one
server or when that cache (database, memcached) is already shared and fast enough.
├── A login attempt, command, or method rather than a route: → RateLimiter facade — guard the action with `RateLimiter::attempt` or
`tooManyAttempts`; the throttle middleware only runs on routes, so non-route
paths call the facade directly (still backed by the chosen cache store).
├── Local development or automated tests only: → array store — in-memory counters reset each request lifecycle; use only in local
dev and tests, never in production where counts must persist across requests.
└── Multiple web servers needing one shared, accurate count: → redis store — a shared Redis cache keeps one atomic counter across all servers, so
the global limit is enforced exactly; the default choice for multi-server apps.
Full engine: references/decision-engine.md — Decision Tree, Priority Matrix, Decision Table.
Workflows
Workflow: Named Limiter
Progress:
- [ ] In a service provider's `boot`, call `RateLimiter::for('api', fn (Request $r) =>
Limit::perMinute(60)->by($r->user()?->id ?: $r->ip()))`
- [ ] Attach the limiter with `Route::middleware('throttle:api')` on the route group so
every endpoint in it shares the named policy
- [ ] Chain `->response(fn () => response()->json(['message' => 'Too many requests'],
429))` on the Limit so throttled API calls get a JSON body plus Retry-After- In a service provider's
boot, callRateLimiter::for('api', fn (Request $r) => Limit::perMinute(60)->by($r->user()?->id ?: $r->ip())).- Verify: The limiter name resolves and keys by user id with an IP fallback.
- Attach the limiter with
Route::middleware('throttle:api')on the route group so every endpoint in it shares the named policy.- Verify:
php artisan route:listshows throttle:api on the group and a burst returns 429.
- Verify:
- Chain
->response(fn () => response()->json(['message' => 'Too many requests'], 429))on the Limit so throttled API calls get a JSON body plus Retry-After.- Verify: Exceeding the limit returns JSON 429 with Retry-After and X-RateLimit-* headers.
Workflow: Redis Store
Progress:
- [ ] Install phpredis or predis and set `CACHE_STORE=redis` (or a dedicated limiter
store) so the RateLimiter's counters live in Redis
- [ ] Send requests through two app servers behind the load balancer and confirm the
combined count trips the limit, proving the counter is shared in Redis
- [ ] Use a separate cache prefix or Redis database for limiter keys, and configure
trusted proxies so `request()->ip()` is the real client behind a load balancer- Install phpredis or predis and set
CACHE_STORE=redis(or a dedicated limiter store) so the RateLimiter's counters live in Redis.- Verify:
config('cache.default')is redis and counters appear as keys in Redis.
- Verify:
- Send requests through two app servers behind the load balancer and confirm the
combined count trips the limit, proving the counter is shared in Redis.
- Verify: The limit trips at the same total across both servers, not once per node.
- Use a separate cache prefix or Redis database for limiter keys, and configure
trusted proxies so
request()->ip()is the real client behind a load balancer.- Verify: Limiter keys do not collide with app cache and IP-keyed limits use the real client IP.
Best practices
- Customize the limiter's
response()callback to return a JSON 429 body with a clear message, and rely on the automaticRetry-AfterandX-RateLimit-*headers so clients can back off - For non-route hot paths (login attempts, a console command, a service method)
guard the action with
RateLimiter::attempt()ortooManyAttempts()instead of the HTTP throttle middleware - Return a
Limitcomputed from the caller in the limiter closure (e.g
Anti-patterns
| Anti-pattern | Why it fails | Do instead |
| One global limit for auth and public endpoints alike | A loose global limit lets credential-stuffing and brute-force attacks run at nearly full speed against auth endpoints. | Give sensitive endpoints their own strict limiter keyed by identifier, separate from the general API limit. |
| Scattering throttle:60,1 literals across routes | Duplicated literals drift apart, hide the real policy, and make per-plan limits impossible to reason about. | Define one named limiter with RateLimiter::for and apply throttle:name so the policy lives in a single place. |
| Hand-rolling rate limits with sleeps or DB counters | Home-grown throttles race under concurrency, block worker or request threads on<br>sleep, and miss the Retry-After and 429 handling Laravel provides. | Use RateLimiter::attempt/tooManyAttempts or the throttle middleware backed by<br>Redis so the counter is atomic and the 429 response is correct. |
See references/anti-patterns.md.
Debugging
- Keying by user id crashes for guests — Guests have no authenticated user, so an unguarded user-id key either errors or lumps all guests together. Fix: Use
->by($request->user()?->id ?: $request->ip())so authenticated callers key by id and guests fall back to their IP. - IP limits throttle everyone behind a proxy — Every request appears to come from the proxy, collapsing per-IP limits into a single global bucket. Fix: Configure trusted proxies (and the correct forwarded headers) so the real client IP is used, or key the limiter by user id where a user is authenticated.
- throttle:name with no registered limiter passes silently — An unknown named limiter resolves to no limit, so the middleware is effectively a no-op. Fix: Register every named limiter in a service provider's boot and assert in a test that a burst against the route returns 429.
- Per-node cache multiplies the effective limit — A non-shared store keeps independent counters, so the global limit is silently multiplied by the server count. Fix: Set the cache (or a dedicated limiter store) to Redis so every server increments the same counter and the cap is enforced globally.
Performance notes
- Per-node cache multiplies the effective limit — With a
fileorarraycache store and several web servers, each node counts hits locally, so a 60/min limit becomes 60 per server. - Back rate limits with Redis across multiple servers — When the app runs on more than one web server, set the cache store used by the
limiter to Redis so counters are shared, not the per-node
fileorarraystore.
Production checklist
- Throttled requests return a 429 with Retry-After and X-RateLimit-* headers, and API limiters return JSON rather than an HTML error page.
- Login, password-reset, and OTP endpoints have their own strict limiter keyed by identifier so credential-stuffing is throttled.
- Behind a load balancer or CDN, trusted proxies are configured so request()->ip() reflects the real client and IP-keyed limits are not all one address.
- Authenticated limiters key by user id with an IP fallback for guests, so shared-proxy users are not throttled as one caller.
- Rate limits are defined as named limiters and applied as throttle:name, not duplicated as throttle:60,1 literals across routes.
- On multi-server deployments the rate-limit store is Redis (or another shared cache), never a per-node file or array store.
Version notes
- CACHE_STORE env key naming across versions — Laravel 11 renamed the cache env key to
CACHE_STORE(fromCACHE_DRIVERin 10.x), which is the store the RateLimiter uses; set it to redis for shared, accurate limits on 10.x–12.x. - RateLimiter facade and named limiters from Laravel 8 — The
RateLimiterfacade withRateLimiter::fornamed limiters and theLimit::perMinutebuilder has been available since Laravel 8 and is supported across the 10.x–12.x range; earlier apps configured throttle only inline.
Security
- Rate-limit auth endpoints and derive keys from trusted input — Put strict, separate limiters on login, password-reset, and OTP endpoints keyed by a stable identifier, and only key limiters by client-supplied values (IP, headers) once trusted proxies are configured so they cannot be spoofed.
Testing
- Activation and decision behavior are verified in
eval/scenarios/laravel/rate-limiting.yaml.
Additional resources
- references/anti-patterns.md
- references/core.md
- references/decision-engine.md
- references/examples.md
- references/pitfalls.md
- references/recipes.md
- references/version-differences.md
Knowledge graph
Prerequisites: databases/redis
Related: security/rate-limiting
<!-- graph-fragment: graph/fragments/laravel/rate-limiting.yaml -->Skill contract
Produces: ratelimit.configured
Risk: low · Confidence: 0.9
<!-- contract-fragment: contracts/fragments/laravel/rate-limiting.yaml -->Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

