Appearance
Rate Limiting
RankHub uses a Durable Object-based rate limiting system (RateLimitDO) for distributed, consistent rate enforcement across all Cloudflare edge locations.
Overview
Rate limiting is implemented as a Cloudflare Durable Object (RateLimitDO in apps/do-worker/src/RateLimitDO.ts). Each rate limit scope gets its own DO instance, providing:
- Sub-20ms response times -- In-memory sliding window counters.
- Distributed consistency -- Durable Object single-threaded guarantee means no race conditions.
- Automatic cleanup -- Expired entries are purged every 5 minutes.
Algorithm
RateLimitDO uses a sliding window counter algorithm:
- Each client (identified by IP hash or user ID) has a counter and window start time.
- On each request, check if the current window has expired. If so, reset the counter.
- If the counter is below the limit, increment and allow the request.
- If the counter exceeds the limit, reject with a
429 Too Many Requestsresponse.
Rate Limit Tiers
API Requests (General)
| Scope | Authenticated | Anonymous | Window |
|---|---|---|---|
api | 30 req/min | 10 req/min | 60s |
Voting
| Scope | Authenticated | Anonymous | Window |
|---|---|---|---|
vote | 30 req/min | 10 req/min | 60s |
Voting uses a hybrid approach: the web app enforces a client-side 2-second cooldown between votes (1 vote / 2s = 30 votes / 60s), while the DO enforces the server-side limit.
Authentication
| Endpoint | Limit | Window |
|---|---|---|
POST /auth/signup | 5 req/hour | 3600s |
POST /auth/signin | 20 req/min | 60s |
POST /auth/forgot-password | 3 req/hour | 3600s |
Response Headers
Rate-limited responses include standard headers:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000060
Retry-After: 42Client Identification
- Authenticated users: Rate limited by user ID (from JWT).
- Anonymous users: Rate limited by SHA-256 hash of IP address +
IP_HASH_SALT. - The salt ensures rate limit keys cannot be reverse-engineered from DO state.
Configuration
Default limits are defined as constants in RateLimitDO.ts:
DEFAULT_MAX_REQUESTS = 30DEFAULT_WINDOW_MS = 60_000(1 minute)CLEANUP_INTERVAL_MS = 300_000(5 minutes)