Rate Limiting
Configure API rate limiting
Rackd includes built-in rate limiting to prevent API abuse and ensure fair resource usage across clients.
Overview
Rate limiting is enabled by default (RATE_LIMIT_ENABLED=true). It tracks request rates per client IP and returns HTTP 429 (Too Many Requests) when limits are exceeded. Disable it with RATE_LIMIT_ENABLED=false for local development.
Configuration
Rate limiting is configured via environment variables:
# Rate limiting (enabled by default)
RATE_LIMIT_ENABLED=true
# Maximum requests per window (default: 100)
RATE_LIMIT_REQUESTS=100
# Time window for rate limiting (default: 1m)
RATE_LIMIT_WINDOW=1m
Example Configurations
Strict limits for public API:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS=60
RATE_LIMIT_WINDOW=1m
Relaxed limits for internal use:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS=1000
RATE_LIMIT_WINDOW=1m
Burst protection:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS=10
RATE_LIMIT_WINDOW=10s
How It Works
Client Identification
Rate limits are applied per client, identified by IP address:
- Forwarded headers (only when
TRUST_PROXY=trueand the direct peer matches aTRUSTED_PROXIESCIDR) - uses the rightmostX-Forwarded-Forentry (the address appended by your own proxy) orX-Real-IP - RemoteAddr (default) - the direct socket address
Headers from untrusted peers are ignored, so clients cannot spoof
X-Forwarded-For to rotate rate-limit buckets. Keying deliberately does
not use the raw Authorization header: anonymous clients could otherwise
rotate fake Bearer tokens for fresh buckets.
Token Bucket Algorithm
Rackd uses a token bucket algorithm:
- Each client gets a bucket with
RATE_LIMIT_REQUESTStokens - Each request consumes one token
- Buckets refill completely after
RATE_LIMIT_WINDOWexpires - Requests are blocked when bucket is empty
No Localhost Bypass
There is no localhost exemption: requests from 127.0.0.1 are limited like
any other client. Health endpoints (/healthz, /readyz) are lightweight
and not rate-limited in practice for typical monitoring intervals; raise
RATE_LIMIT_REQUESTS if a local monitoring agent trips the limiter.
Response Headers
All API responses include rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 2026-02-03T16:45:00Z
When rate limited:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2026-02-03T16:45:00Z
Retry-After: 2026-02-03T16:45:00Z
Content-Type: application/json
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED"
}
Usage Examples
Checking Rate Limit Status
curl -i http://localhost:8080/api/devices
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 2026-02-03T16:45:00Z
Handling Rate Limits
# Make requests until rate limited
for i in {1..150}; do
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/api/devices
done
# Output:
# 200
# 200
# ...
# 200 (100th request)
# 429 (101st request - rate limited)
# 429
# ...
Using API Keys
# Different API keys have separate rate limits
curl -H "Authorization: Bearer key1" http://localhost:8080/api/devices
curl -H "Authorization: Bearer key2" http://localhost:8080/api/devices
Best Practices
For API Consumers
- Monitor headers - Check
X-RateLimit-Remainingto avoid hitting limits - Implement backoff - Respect
Retry-Afterheader when rate limited - Use API keys - Get dedicated rate limits instead of sharing IP-based limits
- Batch operations - Use bulk endpoints to reduce request count
For Administrators
- Keep it enabled - It is on by default; leave it on in production
- Monitor usage - Check logs for rate limit events
- Adjust limits - Tune based on actual usage patterns
- Use reverse proxy - Consider nginx/Caddy rate limiting for additional protection
Monitoring
Rate limit events are logged at DEBUG level:
level=debug msg="Rate limit exceeded" client=192.168.1.100 path=/api/devices
To see rate limit logs:
LOG_LEVEL=debug rackd server
Performance
Rate limiting has minimal overhead:
- Memory: ~100 bytes per active client
- CPU: Negligible (simple token bucket operations)
- Cleanup: Automatic cleanup of inactive clients every 2x window duration
Limitations
- In-memory only - Rate limits reset on server restart
- Single instance - No coordination across multiple Rackd instances
- No per-endpoint limits - All endpoints share the same limit
For multi-instance deployments, consider using a reverse proxy (nginx, Caddy) or API gateway for distributed rate limiting.
Troubleshooting
Rate limiting not working
Check configuration:
# Verify rate limiting is enabled
curl http://localhost:8080/api/config | jq .rate_limit
# Check server logs for rate limit configuration
rackd server
# Output should include:
# level=info msg="Rate limiting enabled" requests=100 window=1m0s
Localhost being rate limited
This should never happen. If it does, check:
- Server is binding to correct address
- Request is actually coming from localhost
- No proxy is rewriting
RemoteAddr
Different rate limits than expected
Rate limits are per-client:
- API key clients get separate limits
- IP-based clients share limits per IP
- Localhost always bypasses limits
Security Considerations
- Not a DDoS solution - Rate limiting helps but isn’t sufficient for DDoS protection
- IP spoofing - Set
TRUST_PROXY=truetogether withTRUSTED_PROXIES(e.g.127.0.0.1); forwarded headers are honored only from those peers - API key sharing - Shared keys share rate limits
- Localhost bypass - Ensure server isn’t exposed with localhost access
See Also
- Authentication - API key management
- Configuration - Environment variables
- Monitoring - Metrics and observability
- Security - Security best practices