Stop Relying on IP-Based Rate Limiting
IP-based rate limiting is outdated and unreliable. Here’s a more practical approach to protecting your API without punishing real users.
Rate limiting is essential. It prevents abuse, protects infrastructure, and ensures fair access. But using IP addresses as the key for rate limiting? That’s often a bad move — and it creates more problems than it solves.
The Problem With IP-Based Limits
IP addresses don't represent users. They represent networks. That distinction matters a lot.
In places like universities, corporate offices, shared housing, or even cafes, multiple users often share the same public IP address. If one of those users starts sending too many requests — intentionally or accidentally — everyone behind that IP gets rate-limited or blocked.
This leads to:
- Frustrated users who haven’t done anything wrong
- Support tickets that are hard to debug
- An API that feels unreliable and inconsistent
Even worse, attackers can rotate IPs, use proxies, or blend in with shared IPs, making your IP-based limits mostly ineffective against real threats.
A Better Approach for Public or Unauthenticated Routes
For unauthenticated routes — like login endpoints, registration forms, or open APIs — you still need some kind of protection. But relying purely on IPs is crude.
A better alternative is fingerprinting. Techniques like JA3/JA4 use characteristics of the client’s TLS handshake or network behavior to create a semi-stable identity. It’s not perfect, but it’s significantly harder to spoof or rotate than IPs.
This helps you:
- More accurately distinguish between users
- Reduce collateral damage from shared IPs
- Catch bots and scrapers that try to cycle through IPs
If fingerprinting is off the table, IP-based limits can still act as a fallback, but should be:
- Short-lived (e.g., 10 requests per 10 seconds, not 100/hour)
- Generous enough to avoid punishing normal users
- Combined with other signals (e.g., user agent, behavior patterns)
For Authenticated Routes: Ditch IPs Completely
Once you’ve identified the user — via an account, token, or API key — IP-based limiting should be thrown out.
At that point, you can (and should) use:
- User ID or API key as the rate limit key
- Granular scopes or tiers (e.g., different limits for free vs. paid users)
- Per-endpoint limits (e.g., stricter on sensitive actions)
This gives you way more control, and avoids blocking legitimate traffic just because a few people are on the same network.
Practical Guidelines
Here’s a cleaner breakdown of what works:
- ✅ Use user ID or API key for any authenticated requests
- ✅ Use fingerprinting (JA3/JA4) for unauthenticated routes
- ✅ Combine multiple signals when needed: fingerprint + IP + behavior
- ✅ Keep limits short-term and generous for public endpoints
- ❌ Don’t treat IPs as user identities — they’re not
Final Thoughts
IP-based rate limiting is easy to implement, which is why it’s so common. But in most cases, it’s also lazy and inaccurate. With better tools like fingerprinting and user-based keys available, there’s no good excuse to keep relying on IPs as your primary rate-limiting strategy.