Rate Limiting for Proxy API
Overview
The frontend proxy API (/api/proxy) now includes rate limiting to prevent abuse and protect the backend from being flooded with requests. The rate limiting is implemented using an in-memory store with configurable limits.
Features
1. General Rate Limiting
- Default: 60 requests per minute per IP address
- Applied to all requests through the proxy
- Returns 429 status code when limit exceeded
- Includes
Retry-Afterheader indicating when to retry
2. Failure Rate Limiting
- Default: 10 failed requests per 5 minutes per IP address
- Only counts requests that result in errors
- Prevents repeated abuse attempts
- Helps identify and block malicious actors
3. Rate Limit Headers
All responses include rate limit information:
X-RateLimit-Remaining: Number of requests remaining in the current windowX-Request-ID: Unique request identifier for trackingX-FailureRateLimit-Remaining: Remaining failure attempts (on error responses)
Configuration
The rate limiters can be configured in /src/lib/rate-limiter.ts:
// Default rate limiter (60 req/min)
export const createDefaultRateLimiter = () => new RateLimiter({
windowMs: 60 * 1000, // 1 minute
maxRequests: 60, // 60 requests per minute
message: 'Too many requests, please try again later'
});
// Failure rate limiter (10 failures/5min)
export const createFailureRateLimiter = () => new RateLimiter({
windowMs: 5 * 60 * 1000, // 5 minutes
maxRequests: 10, // 10 failures per 5 minutes
skipSuccessfulRequests: true,
message: 'Too many failed requests, please try again later'
});
Client Handling
When implementing client-side code that calls the proxy API, handle rate limit responses:
const response = await fetch('/api/proxy', {
method: 'POST',
body: JSON.stringify({ endpoint: '/vac/assistant/123', ... })
});
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const data = await response.json();
// Show error to user
console.error(`Rate limited: ${data.error}. Retry after ${retryAfter} seconds`);
// Optionally implement exponential backoff
setTimeout(() => {
// Retry the request
}, parseInt(retryAfter) * 1000);
}
IP Address Detection
The rate limiter uses the following order to determine client IP:
X-Forwarded-Forheader (first IP if multiple)X-Real-IPheader- Falls back to ‘unknown’ if no IP headers present
Custom Rate Limiters
You can create custom rate limiters for specific use cases:
// Strict rate limiter for sensitive endpoints
const strictLimiter = new RateLimiter({
windowMs: 60 * 1000,
maxRequests: 10,
keyGenerator: (req) => {
// Custom key based on user ID + IP
const userId = req.headers.get('X-User-ID');
const ip = req.headers.get('X-Forwarded-For');
return `${userId}:${ip}`;
}
});
// Burst rate limiter (allows short bursts)
const burstLimiter = new RateLimiter({
windowMs: 1000, // 1 second
maxRequests: 10, // 10 requests per second
});
Memory Management
- Rate limit records are stored in memory
- Expired records are automatically cleaned up every minute
- Memory usage scales with number of unique IPs
- For production deployments with high traffic, consider using Redis-based rate limiting
Testing
Run the rate limiter tests:
npm run test:run src/lib/__tests__/rate-limiter.test.ts
The tests cover:
- Basic rate limiting functionality
- Time window resets
- IP-based tracking
- Selective counting (failures only)
- Custom key generation
- Memory cleanup
Monitoring
Monitor rate limit effectiveness by tracking:
- 429 response rates
X-RateLimit-Remainingvalues in successful responses- Request patterns from specific IPs
- Failed request patterns
Future Enhancements
Consider these improvements for production:
- Redis-based storage for distributed deployments
- User-based rate limiting in addition to IP-based
- Dynamic rate limits based on user tier/subscription
- Webhook notifications for repeated rate limit violations
- Allowlist/blocklist for specific IPs or users