Content Rate Limiting#
The ContentRateLimiter throttles download requests on a per-IP, per-route basis using a sliding window. This prevents a single IP from hammering payload delivery endpoints, which could indicate automated retrieval or a blue team pulling your staged files repeatedly.
How It Works#
Each combination of client IP and route path gets its own sliding window of request timestamps. When a new request arrives, the limiter:
- Prunes timestamps older than the window duration
- Checks if the remaining count exceeds the configured limit
- If over the limit, the request is blocked with a 429 response
The window slides forward continuously, so there is no fixed reset boundary that clients can time around.
Configuration#
Rate limits are set per content route:
content_routes:
- uri: "/downloads/*"
backend:
type: "filesystem"
root: "/opt/payloads"
rate_limit:
requests: 5 # max requests per window
window_seconds: 3600 # 1-hour sliding windowMemory Management#
The limiter tracks timestamps in a defaultdict(deque) keyed by (ip, route). To prevent unbounded memory growth from long-running deployments with many unique visitors:
- When the total number of tracked keys exceeds 50,000, stale entries (those with no recent timestamps) are pruned
- Each key’s deque only holds timestamps within the current window, so old entries are cleaned up on every access
Example#
With requests: 5 and window_seconds: 3600:
| Request # | Time | Result |
|---|---|---|
| 1 | 00:00 | Allowed |
| 2 | 00:15 | Allowed |
| 3 | 00:30 | Allowed |
| 4 | 00:45 | Allowed |
| 5 | 01:00 | Allowed |
| 6 | 01:10 | Blocked (429) |
| 7 | 01:01 | Allowed (request #1 aged out of the window) |
Integration with Content Router#
The rate limiter sits between the ContentRouteResolver (which matches the request to a route) and the content backend (which serves the file). A rate-limited request never reaches the backend.
Request --> ContentRouteResolver --> ContentRateLimiter --> Backend
|
over limit?
|
429 Too Many Requests