Rate Limiting
Protect your HelloJohn instance from brute force attacks, credential stuffing, and abuse with configurable rate limiting.
Rate Limiting
HelloJohn enforces rate limits on all authentication endpoints to prevent brute force attacks, credential stuffing, and abuse.
How rate limiting works
Rate limits are enforced per IP address using a sliding window algorithm. When Redis is configured, rate limit state is shared across all HelloJohn instances. Without Redis, each instance maintains its own in-memory state (not suitable for multi-instance deployments).
When a rate limit is exceeded, HelloJohn returns:
HTTP 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705329785Default limits
| Endpoint | Limit | Window | Scope |
|---|---|---|---|
POST /v1/auth/sign-in | 10 attempts | 1 minute | Per IP |
POST /v1/auth/sign-up | 5 attempts | 1 hour | Per IP |
POST /v1/auth/magic-link | 5 requests | 10 minutes | Per email + IP |
POST /v1/auth/password-reset | 3 requests | 1 hour | Per email |
POST /v1/auth/token/refresh | 60 requests | 1 minute | Per IP |
POST /v1/auth/mfa/verify | 5 attempts | 5 minutes | Per user session |
GET /v1/auth/oauth/callback | 30 requests | 1 minute | Per IP |
| Global API | 300 requests | 1 minute | Per IP |
Configuration
# Enable/disable rate limiting globally
RATE_LIMIT_ENABLED=true
# Login endpoint
RATE_LIMIT_LOGIN_MAX=10
RATE_LIMIT_LOGIN_WINDOW=1m
# Signup endpoint
RATE_LIMIT_SIGNUP_MAX=5
RATE_LIMIT_SIGNUP_WINDOW=1h
# Magic link
RATE_LIMIT_MAGIC_LINK_MAX=5
RATE_LIMIT_MAGIC_LINK_WINDOW=10m
# Global API rate limit
RATE_LIMIT_GLOBAL_MAX=300
RATE_LIMIT_GLOBAL_WINDOW=1mAccount lockout
In addition to IP-based rate limits, HelloJohn implements per-account lockout after repeated failures:
| Failures | Action | Duration |
|---|---|---|
| 5 in 5 min | Soft lock (CAPTCHA required) | Until CAPTCHA solved |
| 10 in 10 min | Hard lock (email required) | Until email unlock link clicked |
| 20 in 1 hour | Account disabled | Until admin re-enables |
Configure lockout thresholds:
ACCOUNT_LOCKOUT_SOFT_THRESHOLD=5
ACCOUNT_LOCKOUT_SOFT_WINDOW=5m
ACCOUNT_LOCKOUT_HARD_THRESHOLD=10
ACCOUNT_LOCKOUT_HARD_WINDOW=10m
ACCOUNT_LOCKOUT_DISABLE_THRESHOLD=20
ACCOUNT_LOCKOUT_DISABLE_WINDOW=1hIP allowlist and blocklist
Blocklist
Block specific IPs or CIDR ranges from all authentication endpoints:
POST /v1/security/ip-blocklist
Authorization: Bearer <admin_api_key>
Content-Type: application/json
{
"ip": "203.0.113.42",
"reason": "Credential stuffing attack",
"expires_at": "2024-02-01T00:00:00Z" // null = permanent
}
# Block a CIDR range
{
"ip": "203.0.113.0/24",
"reason": "Datacenter IP range"
}Allowlist (bypass rate limits)
Allow trusted IPs (e.g., your test infrastructure) to bypass rate limits:
POST /v1/security/ip-allowlist
Authorization: Bearer <admin_api_key>
Content-Type: application/json
{
"ip": "10.0.0.0/8",
"reason": "Internal test network"
}Environment variable blocklist
For self-hosted deployments, set blocked IPs at startup:
IP_BLOCKLIST=203.0.113.42,198.51.100.0/24
IP_ALLOWLIST=10.0.0.0/8,172.16.0.0/12Trusted proxies
If HelloJohn is behind a reverse proxy, configure it to trust the X-Forwarded-For header for accurate IP-based rate limiting:
TRUST_PROXY=true
TRUST_PROXY_HOPS=1 # number of trusted proxy hopsWithout this, all requests appear to come from the proxy's IP and rate limiting is ineffective.
Rate limiting in multi-instance deployments
When running multiple HelloJohn instances, a shared Redis instance is required for consistent rate limiting:
REDIS_URL=redis://redis:6379Without Redis, each instance applies rate limits independently. A client could bypass per-IP limits by distributing requests across instances.
Monitoring rate limit events
Rate limit violations are recorded in the audit log as security.rate_limit_exceeded:
{
"type": "security.rate_limit_exceeded",
"actor": { "type": "anonymous" },
"ip_address": "203.0.113.42",
"metadata": {
"endpoint": "/v1/auth/sign-in",
"limit": 10,
"window": "1m",
"attempt_count": 11
}
}Set up an alert when security.rate_limit_exceeded events exceed a threshold — this is a leading indicator of a credential stuffing attack.
Custom rate limits per tenant
Enterprise tenants can have custom rate limits configured by an admin:
PATCH /v1/tenants/{tenant_id}/config
{
"rate_limits": {
"sign_in_max": 20,
"sign_in_window": "1m",
"sign_up_max": 10,
"sign_up_window": "1h"
}
}Recommended production configuration
For most production deployments:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_LOGIN_MAX=10
RATE_LIMIT_LOGIN_WINDOW=1m
RATE_LIMIT_SIGNUP_MAX=5
RATE_LIMIT_SIGNUP_WINDOW=1h
RATE_LIMIT_MAGIC_LINK_MAX=5
RATE_LIMIT_MAGIC_LINK_WINDOW=10m
ACCOUNT_LOCKOUT_SOFT_THRESHOLD=5
ACCOUNT_LOCKOUT_HARD_THRESHOLD=10
TRUST_PROXY=true
REDIS_URL=redis://redis:6379