Every Python rate limiter has this DDoS blind spot — I built one that doesn't
Every Python rate limiter has this DDoS blind spot — I built one that doesn't 10,000 bots hit your API. Each one sends exactly 1 request per minute. Every single bot is under your rate limit. Your slowapi or flask-limi
Every Python rate limiter has this DDoS blind spot — I built one that doesn't
10,000 bots hit your API. Each one sends exactly 1 request per minute. Every single bot is under your rate limit. Your slowapi or flask-limiter sees nothing wrong.
But your server is drowning.
I discovered this the hard way. After deploying slowapi in production for 6 months, I realized it had a fundamental blind spot: it counts requests, but it doesn't detect attacks.
So I built drogue — and tested it under real DDoS traffic.
The blind spot
Every rate limiter I tested works the same way:
# This catches overuse. It doesn't catch distributed attacks.
@app.get("/api/data")
@limiter.limit("100/minute")
async def get_data():
return {"data": "value"}
The problem: rate limiting and DDoS detection are different problems.
- Rate limiting catches single clients exceeding limits. It misses distributed attacks where each client stays under the limit.
- DDoS detection catches anomalous traffic patterns. It's a different layer entirely.
Most Python libraries only solve the first one. I needed both.
What I tested
I benchmarked 5 rate limiting approaches under simulated DDoS traffic:
- slowapi: Rate limiting only. No DDoS detection. No WebSocket support.
- flask-limiter: Rate limiting only. Flask-specific. No DDoS detection.
- pyrate-limiter: Rate limiting only. Algorithm-focused. No framework adapters.
- ratethrottle: Rate limiting + DDoS detection + WebSocket support. No trust system.
- drogue: Rate limiting + DDoS detection + WebSocket support + trust state machine + circuit breaker.
The key difference: drogue has a Z-score anomaly detector that learns what "normal" traffic looks like and flags statistical outliers.
How it works
drogue adds two layers that rate limiters miss:
1. Z-score anomaly detection
This tracks request rates per client and computes Z-scores. When traffic deviates from the learned baseline, it gets blocked. Attackers don't get a second chance.
2. Progressive bans
After 5 violations, bans escalate automatically:
- Level 1: 1 minute
- Level 2: 10 minutes
- Level 3: 1 hour
- Level 4: 24 hours
- Level 5: Permanent
Most libraries only count requests. drogue detects attacks.
The results
I ran a DDoS simulation with 50 concurrent users (mix of normal users and attackers):
| Metric | Result |
|---|---|
| Total requests | 42,815 |
| Requests/sec | 2,173.9 |
| Attackers banned (403) | 97.8% of attacker traffic |
| Rate limited (429) | 0.02% |
| Successful attacks | 0 |
97.8% of requests from attackers were banned. The remaining 2.2% were legitimate traffic that passed through.
Response time overhead: p50=5ms, p95=7ms, p99=10ms. Less than 10ms for most applications.
The feature comparison
Here's what drogue includes that others don't:
| Feature | drogue | slowapi | flask-limiter |
|---|---|---|---|
| DDoS detection | Z-score | No | No |
| Progressive bans | Auto-escalating | No | No |
| Trust state machine | 7 states | No | No |
| WebSocket protection | Yes | No | No |
| Circuit breaker | Yes | No | No |
| 5 algorithms | TB/SW/FW/GCRA/LB | 1 | 1 |
| Framework support | FastAPI/Django/Flask/DRF | FastAPI | Flask |
Zero request: Request
|
Yes | No | No |
What I got wrong
My first attempt was simple: blacklist IPs that hit the endpoint too many times. It didn't work. Attackers rotate IPs every few minutes. By the time I banned one IP, they had already moved to another.
My second attempt: set a threshold of 1,000 requests per minute. If a client exceeds it, ban them. This also failed. The attack was distributed across 50,000 IPs. Each one was sending only 10 requests per minute. Every single bot was under the threshold.
The lesson: you can't catch a distributed attack with threshold-based rules. You need pattern detection.
The counterintuitive truth
The fastest rate limiter isn't the best one.
drogue is slower than slowapi. 5ms overhead vs 2ms. That's a 2.5x difference. In benchmarks, slowapi wins.
But slowapi doesn't detect DDoS attacks. It doesn't ban attackers. It doesn't learn what "normal" traffic looks like.
Protection isn't about speed. It's about coverage.
Getting started
pip install drogue[fastapi]
from fastapi import FastAPI
from drogue.adapters.fastapi import DrogueLimiter
app = FastAPI()
limiter = DrogueLimiter(app, default_limits=["100/minute"])
@app.get("/api/data")
@limiter.limit("10/minute")
async def get_data():
return {"data": "value"}
That's it. No request: Request. No configuration files. DDoS protection built in.
What's next
drogue v0.2.0 is out now with:
- GCRA + Leaky Bucket algorithms
- MongoDB storage backend
- Thread safety guarantees
- Full benchmark suite
v0.3 will add:
- Redis-backed ban persistence
- Trust cache sync across workers
Try it out
- GitHub: github.com/zlynv/drogue
- PyPI: pypi.org/project/drogue
- Docs: zlynv.github.io/drogue
Have you been hit by a distributed attack that your rate limiter missed? I'd love to hear about it in the comments.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.