Skip to content

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:

  1. Each client (identified by IP hash or user ID) has a counter and window start time.
  2. On each request, check if the current window has expired. If so, reset the counter.
  3. If the counter is below the limit, increment and allow the request.
  4. If the counter exceeds the limit, reject with a 429 Too Many Requests response.

Rate Limit Tiers ​

API Requests (General) ​

ScopeAuthenticatedAnonymousWindow
api30 req/min10 req/min60s

Voting ​

ScopeAuthenticatedAnonymousWindow
vote30 req/min10 req/min60s

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 ​

EndpointLimitWindow
POST /auth/signup5 req/hour3600s
POST /auth/signin20 req/min60s
POST /auth/forgot-password3 req/hour3600s

Response Headers ​

Rate-limited responses include standard headers:

X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000060
Retry-After: 42

Client 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 = 30
  • DEFAULT_WINDOW_MS = 60_000 (1 minute)
  • CLEANUP_INTERVAL_MS = 300_000 (5 minutes)