shared-network-rate-limits
Skill0 starsChanged 48 days agoMITView on GitHub
Rate limit Laravel forms, OTP/SMS/M-Pesa prompts, status polling, downloads and APIs per person (email, phone, resource, user) instead of per IP, so thousands of mobile users behind one carrier IP (CGNAT), office or campus network don't lock each other out. Use when adding throttling, RateLimiter::for, 429 Too Many Requests errors, brute-force or spam protection, or when legitimate users report being blocked.
In this repository11 other files it gives its agents.
- bot-proof-formsSkill
- image-uploadsSkill
- inertia-admin-tablesSkill
- json-ld-structured-dataSkill
- mpesa-stk-pushSkill
- paypal-checkoutSkill
- pre-push-checksSkill
- secrets-scannerSkill
- seo-crawlabilitySkill
- social-share-previewsSkill
- wordpress-migrationSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 34
Update any existing test that expected a 429 after N requests from different people.
- Distinctive: line 38
Every per-IP limit silently becomes site-wide if the app sees the proxy's IP instead of the visitor's.
- Very specific: line 48
Check it: log $request->ip() from two different phones on mobile data.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Rate limits that don't punish shared networks
throttle:5,1 counts per IP address. That is fine on a home connection and wrong almost everywhere else. Mobile carriers put thousands of phones behind a handful of public IPs (carrier-grade NAT), and so do offices, schools, campus Wi-Fi and some countries' entire mobile networks. One busy minute, and strangers use up each other's allowance.
This skill registers named limiters that count per person first (email, phone number, the resource being polled, the signed-in user), with a generous per-IP backstop that only bots reach.
| Limiter | Per person | Per IP backstop | Use on |
forms | 3/min per email (or phone, username) per form | 30/min | contact, newsletter, sign-up, password reset |
phone-prompts | 3 per 5 min per phone number | 30/min | OTP, SMS, WhatsApp, M-Pesa STK push |
polling | 30/min per URL (each resource) | 600/min | payment and job status endpoints |
downloads | – | 120/min | file downloads |
api | 120/min per user when signed in | 120/min for guests | API routes |
Install
- Copy
stubs/app/Providers/RateLimitServiceProvider.php.stubtoapp/Providers/RateLimitServiceProvider.php. - Register it in
bootstrap/providers.php(Laravel 11+) or inconfig/app.phpproviders (Laravel 10). - Replace numeric throttles on routes:
Route::post('subscribe', ...)->middleware('throttle:forms'); Route::post('otp/send', ...)->middleware('throttle:phone-prompts'); Route::get('payments/{payment:reference}', ...)->middleware('throttle:polling'); - Copy the test to
tests/Feature/RateLimitsTest.phpand run it. It proves 20 people on one IP can all subscribe, while one person is still limited. - Update any existing test that expected a 429 after N requests from different people. That was the bug.
Make sure $request->ip() is the visitor's IP
Every per-IP limit silently becomes site-wide if the app sees the proxy's IP instead of the visitor's.
- Served directly (cPanel, a VPS with nginx or Apache): nothing to do.
- Behind a load balancer, Cloudflare or a CDN: trust the proxy, or everyone shares one limit. In
bootstrap/app.php:
With Cloudflare, prefer trusting only Cloudflare's published IP ranges. The real visitor IP is in->withMiddleware(function (Middleware $middleware) { $middleware->trustProxies(at: '*'); // or the proxy's IP ranges })CF-Connecting-IP. - Check it: log
$request->ip()from two different phones on mobile data. The values must differ.
Rules
- Limit per identity for the action, per IP only as a backstop. Validation still runs after the limiter, so an identity-less request only gets the IP limit.
- Normalise identities.
Same@Example.organdsame@example.orgare one person.0712 345 678and+254712345678are one phone (last nine digits). - Key
pollingby path, so each payment or job has its own allowance. With per-IP polling, five customers on one network checking every four seconds (75 requests a minute) would block each other. - Throttling runs before route model binding. Inside a limiter,
$request->route('payment')is still the raw string, not a model. Use$request->route()->originalParameter('payment')or the path. - Give each
Limitin an array a distinct key prefix. Two limits with the same key share one counter. - The cache store is the counter.
database,redisandmemcachedwork across servers. Thefiledriver only works on a single server, andarrayresets every request. - Page views don't need throttling. Limit actions (submissions, sends, payments, downloads), not reading.
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.

