Production-ready rate limiting and DDoS protection for Python web frameworks
Project description
drogue
Rate limiting and DDoS protection for Python web applications. Clean APIs, WebSocket support, and built-in defense layers.
What problem does drogue solve?
Web applications need rate limiting to prevent abuse, but existing solutions have gaps:
-
Signature pollution -- Most rate limiters force
request: Requestinto every function signature, coupling your business logic to the rate limiter. -
No WebSocket protection -- Real-time applications using WebSockets have no built-in rate limiting.
-
No DDoS detection -- Simple counters catch over-use, but cannot detect distributed attacks where each client stays below the limit.
-
No trust differentiation -- Every request goes through the same evaluation path, even for verified users.
drogue addresses all four. It rate-limits by identity (IP, user, header) without touching your function signatures, detects anomalous traffic patterns, and fast-tracks trusted clients.
Who is drogue for?
- API developers who need rate limiting without framework lock-in
- Teams running FastAPI, Django, or Flask who want a single solution across all three
- Platforms facing DDoS or abuse that need more than simple request counting
- Applications with WebSocket connections that need real-time protection
How it works
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"}
No request: Request parameter. Rate limit headers are injected automatically. The same pattern works for Flask and Django.
Request flow:
- Client sends a request
- drogue extracts the client key (IP, user ID, or custom header)
- Rules are matched against the route
- The algorithm evaluates the request (Token Bucket, Sliding Window, or Fixed Window)
- Protection layers run (DDoS check, trust state, probe detection)
- Response is returned with rate limit headers
Features
| Category | What you get |
|---|---|
| Rate Limiting | Token Bucket, Sliding Window, Fixed Window, cost-aware limits, blocking mode, burst control |
| Identity | IP-based, user-based, header-based, composite extractors, anti-spoof X-Forwarded-For |
| Frameworks | FastAPI (ASGI), Flask (decorator), Django (middleware + decorator), Django REST Framework (throttle) |
| DDoS Detection | Z-score anomaly detection, streaming Sentinel Model, probe pattern detection |
| Auto-Ban | Progressive ban with doubling duration (5m to 160m), configurable thresholds |
| Trust System | State machine (Unknown/Normal/Trusted/Distrusted/Banned), 9x throughput for verified users |
| Circuit Breaker | Closed/Open/HalfOpen states, automatic recovery |
| Adaptive Limits | CPU and memory-based scaling, reduces limits under system load |
| Defense Randomization | Per-session variance, honeypot paths, anti-fingerprinting |
| CIDR Filtering | Allow/block lists from config or files, IPv4 and IPv6 |
| Probabilistic Storage | Count-Min Sketch (10MB for 1M keys), Bloom Filter, Cuckoo Filter, HyperLogLog |
| Observability | Prometheus metrics, OpenTelemetry tracing, structured JSON logging |
| Shadow Mode | Test rules without enforcing, collect metrics before go-live |
Performance
| Metric | drogue | Notes |
|---|---|---|
| Token Bucket | ~1.4us | median latency per acquire |
| Sliding Window | ~1.6us | median latency per acquire |
| Fixed Window | ~1.1us | median latency per acquire |
| Throughput | 700K+ req/s | single worker, in-memory storage |
| Memory per key | ~150 bytes | in-process storage |
Measured on Intel Core Ultra 5 225F, Python 3.13, asyncio single-worker, in-memory storage, 100K iterations.
Install
pip install drogue
# With framework extras
pip install drogue[fastapi] # FastAPI + Starlette
pip install drogue[django] # Django
pip install drogue[flask] # Flask
pip install drogue[drf] # Django REST Framework
pip install drogue[redis] # Redis backend
pip install drogue[all] # Everything
Quick Start
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"}
Flask:
from flask import Flask
from drogue.adapters.flask import DrogueLimiter
app = Flask(__name__)
limiter = DrogueLimiter(app, default_limits=["100/minute"])
@app.route("/api/data")
@limiter.limit("10/minute")
def get_data():
return {"data": "value"}
Django:
# settings.py
MIDDLEWARE = [
"drogue.adapters.django.DrogueMiddleware",
]
# views.py
from drogue.adapters.django import DrogueRateLimiter
from django.http import JsonResponse
limiter = DrogueRateLimiter()
@limiter.limit("10/minute")
def get_data(request):
return JsonResponse({"data": "value"})
Migration from slowapi
# Before (slowapi)
from slowapi import Limiter
from slowapi.util import get_remote_address
from starlette.requests import Request
limiter = Limiter(key_func=get_remote_address)
@app.get("/api/data")
@limiter.limit("10/minute")
async def get_data(request: Request):
return {"data": "value"}
# After (drogue)
from drogue.adapters.fastapi import DrogueLimiter
limiter = DrogueLimiter(app)
@app.get("/api/data")
@limiter.limit("10/minute")
async def get_data():
return {"data": "value"}
Known Limitations
- Ban state is in-memory only by default. Redis persistence planned for v0.3.
- Trust cache is per-process. Multi-worker setups need separate trust state per worker.
- Flask headers for dict-returning views do not inject automatically.
Security
Report security vulnerabilities via GitHub's private vulnerability reporting. Do not open public issues for security bugs. Response time: 48 hours.
Roadmap
- v0.1 -- Core rate limiting, DDoS detection, three frameworks (current)
- v0.2 -- Redis-backed ban state, WebSocket support for Django and Flask
- v0.3 -- Trust cache cross-process sync, advanced Sentinel features
- v1.0 -- Production-ready, full documentation site
Development
pip install drogue[dev]
pytest
ruff check src/drogue/
License
MIT License. See LICENSE for details.
Created by Zlynv
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file drogue-0.2.0.tar.gz.
File metadata
- Download URL: drogue-0.2.0.tar.gz
- Upload date:
- Size: 115.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1781eccacebc1e186d5612ad746b21dcffe74fbe0361e5e5a4c6fa63d8f64072
|
|
| MD5 |
6457b36f29c9a3877f213337de680683
|
|
| BLAKE2b-256 |
c96f2db118fc3ecbc62c2fb7a4275be58145f25dc9f5277d646d28fca175558a
|
File details
Details for the file drogue-0.2.0-py3-none-any.whl.
File metadata
- Download URL: drogue-0.2.0-py3-none-any.whl
- Upload date:
- Size: 77.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b66759b6bfd656f264a6a956acb07ea5f04665ca84396e6e4b910c9446ba8cf
|
|
| MD5 |
e41728e2fad85c7dd867faca02115d23
|
|
| BLAKE2b-256 |
0b3726861af27b780c8e9b71a4ff593d6f2889b37fcc564eacb7dcc48fc2ceeb
|