Better Auth in Rust

Rate Limiting

Protect your authentication endpoints from abuse with rate limiting.

Better Auth includes a built-in rate limiter that restricts the number of requests a client can make within a time window. This protects authentication endpoints from brute-force attacks and abuse.

Setup

use better_auth::RateLimitConfig;
use std::time::Duration;

let auth = BetterAuth::new(config)
    .rate_limit(
        RateLimitConfig::new()
            .default_limit(Duration::from_secs(60), 100)
            .endpoint("/sign-in/email", Duration::from_secs(60), 10)
            .endpoint("/sign-up/email", Duration::from_secs(60), 5)
            .endpoint("/forget-password", Duration::from_secs(300), 3)
    )
    .build()
    .await?;

Configuration

OptionTypeDefaultDescription
windowDuration60sDefault time window
max_requestsu32100Default max requests per window
per_endpointHashMap{}Per-endpoint overrides
enabledbooltrueEnable/disable rate limiting

Per-Endpoint Limits

Different endpoints have different risk profiles. Set stricter limits on sensitive endpoints:

RateLimitConfig::new()
    // General API: 100 req/min
    .default_limit(Duration::from_secs(60), 100)
    // Sign-in: 10 req/min (brute-force protection)
    .endpoint("/sign-in/email", Duration::from_secs(60), 10)
    .endpoint("/sign-in/username", Duration::from_secs(60), 10)
    // Sign-up: 5 req/min (spam protection)
    .endpoint("/sign-up/email", Duration::from_secs(60), 5)
    // Password reset: 3 req/5min (abuse protection)
    .endpoint("/forget-password", Duration::from_secs(300), 3)
    // 2FA verification: 5 req/min
    .endpoint("/two-factor/verify-totp", Duration::from_secs(60), 5)
    .endpoint("/two-factor/verify-otp", Duration::from_secs(60), 5)
    .endpoint("/two-factor/verify-backup-code", Duration::from_secs(60), 5)

Client Identification

The rate limiter identifies clients using the following headers, in order of priority:

  1. X-Forwarded-For — The first IP in the forwarded chain
  2. X-Real-IP — Set by reverse proxies
  3. Default bucket — Fallback when no IP header is present

Make sure your reverse proxy (Nginx, Caddy, etc.) correctly sets the X-Forwarded-For or X-Real-IP headers. Without these, all clients may share the same rate limit bucket.

Response

When a client exceeds the rate limit, a 429 Too Many Requests response is returned:

{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Too many requests",
  "retryAfter": 45
}
FieldDescription
codeError code: RATE_LIMIT_EXCEEDED
messageHuman-readable message
retryAfterSeconds until the client can retry
Endpoint CategoryWindowMax RequestsRationale
Sign-in60s10Brute-force protection
Sign-up60s5Spam prevention
Password reset300s3Email abuse prevention
2FA verification60s5Code guessing prevention
OAuth sign-in60s20Higher limit for redirects
General API60s100Default protection
Admin endpoints60s50Moderate protection

Disabling Rate Limiting

For development or testing, you can disable rate limiting entirely:

RateLimitConfig::new().enabled(false)

Never disable rate limiting in production. Authentication endpoints are prime targets for brute-force and credential stuffing attacks.

See Also

  • Middleware — All built-in middleware including rate limiting
  • Security — Security best practices

On this page