This document describes the comprehensive rate limiting system implemented in the server application.
The rate limiting system uses Redis as a backend storage mechanism to track request counts and implement cooldown periods. It provides five different rate limiting tiers to protect various types of endpoints with appropriate restrictions.
src/core/shared/rate_limiter.rs)src/core/shared/middleware.rs)src/main.rs)redis_sessions) for rate limiting datarate_limit_email)Purpose: Strictest protection for email-related endpoints to prevent spam
Configuration:
rl:email:{ip}Implementation:
// 60 second cooldown for email endpoints
if let Ok(Some(response)) = is_rate_limited(&mut conn, &key, 60).await {
return response;
}
rate_limit_strict)Purpose: High-value endpoints requiring strong protection
Configuration:
rl:strict:{path_with_underscores}:{ip}Implementation:
// 3600 seconds = 1 hour cooldown for strict limits
if let Ok(Some(response)) = is_rate_limited(&mut conn, &key, 3600).await {
return response;
}
rate_limit_api)Purpose: External API calls to control costs and prevent abuse
Configuration:
rl:api:{path_with_underscores}:{ip}Implementation:
// 2 second cooldown allows ~30 req/min
if let Ok(Some(response)) = is_rate_limited(&mut conn, &key, 2).await {
return response;
}
rate_limit_moderate)Purpose: Verification and business logic endpoints
Configuration:
rl:moderate:{path_with_underscores}:{ip}Implementation:
// 6 second cooldown allows ~10 req/min
if let Ok(Some(response)) = is_rate_limited(&mut conn, &key, 6).await {
return response;
}
rate_limit_general)Purpose: Standard protection for general endpoints
Configuration:
rl:general:{ip}Implementation:
// 1 second cooldown allows ~60 req/min
if let Ok(Some(response)) = is_rate_limited(&mut conn, &key, 1).await {
return response;
}
/health - Health check endpoint/ready - Readiness probe/demo/health - Demo health check/api/config/captcha - Captcha configuration/api/core/*) - All core functionality/demo/nonce - Nonce generation/demo/captcha-config - Demo captcha config/demo/vendor-inquiry - Vendor inquiry submission (Recently changed from strict)/demo/loi/start - LOI process initiation/demo/loi/verify - Email and captcha verification/demo/loi/check - Business binding verification/demo/waitlist - Waitlist signup/demo/loi/create - Business creation from place/demo/loi/submit - LOI submission/demo/places/autocomplete - Google Places autocomplete/demo/places/details - Google Places detailsThe is_rate_limited function in src/core/shared/rate_limiter.rs implements the core logic:
pub async fn is_rate_limited(
conn: &mut MultiplexedConnection,
key: &str,
cooldown_seconds: usize,
) -> Result<Option<Response>, redis::RedisError> {
// Check if key exists (previous request within cooldown)
if let Ok(true) = conn.exists(key).await {
return Ok(Some((
StatusCode::TOO_MANY_REQUESTS,
format!("Please wait {} seconds before retrying", cooldown_seconds),
).into_response()));
}
// Set key with expiry to start cooldown period
let _: () = conn.set_ex(key, "1", cooldown_seconds as u64).await?;
Ok(None) // Allow request through
}
When Redis is unavailable, the middleware:
Rate-limited requests receive:
"Please wait {cooldown_seconds} seconds before retrying"The system uses the client_ip helper function to determine the user's IP address:
X-Forwarded-For headers from trusted proxiesTrusted proxy configuration is loaded from environment variables during startup.
redis_sessions)REDIS_PASSWORD env var)REDIS_IP and REDIS_PORT env varsREDIS_IP: Redis server IP (default: "localhost")REDIS_PORT: Redis server port (default: "6379")REDIS_PASSWORD: Redis password (default: empty/no auth)"Redis connection error in {function_name}: {error}"You can inspect active rate limits using Redis CLI:
# View all rate limiting keys
redis-cli KEYS "rl:*"
# Check specific IP's general rate limit
redis-cli EXISTS "rl:general:192.168.1.1"
# View TTL for a key
redis-cli TTL "rl:strict:_demo_waitlist:192.168.1.1"
To reset rate limits for testing:
# Clear all rate limiting data
redis-cli FLUSHDB
# Clear specific IP
redis-cli DEL "rl:general:192.168.1.1"
# Clear all strict rate limits
redis-cli EVAL "return redis.call('del', unpack(redis.call('keys', 'rl:strict:*')))" 0