Skip to content

Rate Limiter

Overview

The rate limiter protects your API from burst abuse and accidental flooding. It applies a global rate limit per session (or IP for anonymous visitors) and a stricter login limiter on public auth routes to mitigate brute-force attacks.

Both limiters use Redis when available and gracefully fall back to in-memory storage.

Global Rate Limiter

Registered automatically in the HTTP middleware stack via rateLimiterMiddleware. Every request consumes from the same per-session/IP bucket:

ts
// src/framework/http/app.ts — applied globally
app.use("*", rateLimiterMiddleware);

Key generation

  • Authenticated requests are keyed by sessionId.
  • Anonymous requests fall back to the x-forwarded-for header.

Login Limiter

The loginLimiter is applied to all public auth routes (/register, /login, /forgot-password, /reset-password, /verify-email, /refresh-token) in src/modules/auth/routes/api.ts:

ts
const publicRoute = createRouter()
  .group(loginLimiter)
  .api(registerRoute, register)
  .api(loginRoute, login)
  // ...

It uses IP-based keying so a single IP cannot exceed the login limit regardless of how many sessions it opens.

Changing Limits

Edit src/config/rateLimit.ts:

ts
export const rateLimitConfig = {
  windowMs: 60000,       // 1 minute window
  maxRequests: 500,      // per window (global)
  loginMaxRequests: 60,  // per window (login, per IP)
  keyPrefix: `${redisConfig.prefix}:rl`,
};

Per-route customization

To apply a different limit to a specific route group, create a new limiter in your route file:

ts
import { rateLimiter, MemoryStore } from "hono-rate-limiter";

const uploadLimiter = rateLimiter({
  windowMs: 60_000,
  limit: 20,
  keyGenerator: (c) => c.req.header("x-forwarded-for") ?? "unknown",
  store: new MemoryStore(),
  standardHeaders: "draft-6",
});

router.post("/upload", uploadLimiter, uploadHandler);

Source files

FileWhat it controls
src/config/rateLimit.tsRate limit settings (window, max, login max)
src/framework/http/ratelimiter.tsCore limiter logic — lazy singleton, store selection, key generation
src/framework/http/app.tsWhere rateLimiterMiddleware is registered globally
src/modules/auth/routes/api.tsWhere loginLimiter is applied to public auth routes

Environment Variables

VariableDefaultDescription
REDISfalseEnable Redis for persistent rate limiting across restarts
REDIS_URLredis://127.0.0.1:6379Redis connection string
REDIS_PREFIXnexgenKey prefix for namespacing

How It Works

When Redis is available (REDIS=true), limits are stored in Redis under keys prefixed with {REDIS_PREFIX}:rl:. This ensures limits survive server restarts and are consistent across multiple instances.

When Redis is unavailable, a MemoryStore is used instead — limits reset on server restart.

Both limiters send standard RateLimit-* headers (draft-6 format) so clients can programmatically back off.

Released under the MIT License.