Redis-Backed Rate Limits and Tighter Auth-Route Guards
Rate Limiting
koa-ratelimit counts requests per identifier in Redis and returns 429 Too Many Requests with standard headers when the limit is exceeded. Auth routes deserve tighter thresholds than general API routes.
What you'll learn
- Configure koa-ratelimit with a Redis store
- Apply a stricter rate limit specifically to login and registration routes
- Return 429 with standard RateLimit-* headers
Rate limiting is a critical control against brute-force login attacks,
credential stuffing, and API abuse. Without it, an attacker can try millions
of password combinations against your /login endpoint.
Installation
npm install koa-ratelimit ioredis Basic Setup
import Koa from "koa";
import ratelimit from "koa-ratelimit";
import Redis from "ioredis";
const app = new Koa();
const redis = new Redis(process.env.REDIS_URL);
// Global limit: 300 requests per minute per IP.
app.use(
ratelimit({
driver: "redis",
db: redis,
duration: 60_000, // window in ms
max: 300, // requests per window
id: (ctx) => ctx.ip, // key by IP
headers: {
remaining: "RateLimit-Remaining",
reset: "RateLimit-Reset",
total: "RateLimit-Limit",
},
errorMessage: "Too many requests — please slow down.",
disableHeader: false,
})
); Tighter Limits on Auth Routes
Apply a separate, stricter limiter as route-level middleware on auth endpoints.
const authLimiter = ratelimit({
driver: "redis",
db: redis,
duration: 15 * 60_000, // 15-minute window
max: 10, // only 10 attempts per window
id: (ctx) => `auth:${ctx.ip}`,
errorMessage: "Too many login attempts — try again in 15 minutes.",
});
router.post("/auth/login", authLimiter, async (ctx) => {
// login logic
});
router.post("/auth/register", authLimiter, async (ctx) => {
// registration logic
}); Response Headers
koa-ratelimit sets standard headers on every response:
| Header | Meaning |
|---|---|
RateLimit-Limit | Maximum requests allowed in the window |
RateLimit-Remaining | Requests left in the current window |
RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds until reset (on 429 responses) |
These headers let clients back off gracefully rather than hammering the server.
Keying Strategies
The id function determines how requests are grouped. Common strategies:
// By IP (default — good for anonymous routes)
id: (ctx) => ctx.ip,
// By authenticated user ID (prevents shared-IP false positives)
id: (ctx) => ctx.state.user?.sub ?? ctx.ip,
// By IP + route (isolates per-endpoint budgets)
id: (ctx) => `${ctx.ip}:${ctx.path}`, Up Next
Validate and sanitize all incoming input to prevent injection attacks and XSS before data reaches your business logic.
Input Validation & Sanitization →