Rate Limiting in Nuxt That Actually Works
Nuxt 3 doesn't ship a rate-limit module, but Nitro's event handlers + Upstash Redis give you production-grade rate limiting in 30 lines.
Rate Limiting in Nuxt That Actually Works
Nuxt 3 doesn't ship rate limiting; you build it on top of Upstash Redis or another HTTP-accessible store. Here's the 30-line version.
Setup
npm install @upstash/ratelimit @upstash/redis
Set environment variables: UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN.
The middleware
// server/middleware/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const redis = Redis.fromEnv();
const generalLimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(100, '1 m'),
prefix: '@app/general',
});
const authLimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(5, '5 m'),
prefix: '@app/auth',
});
export default defineEventHandler(async (event) => {
const path = getRequestURL(event).pathname;
if (!path.startsWith('/api/')) return;
const ip = getRequestHeader(event, 'x-forwarded-for')?.split(',')[0]?.trim()
|| getRequestIP(event)
|| 'unknown';
const limit = path.startsWith('/api/auth') ? authLimit : generalLimit;
const { success, limit: max, remaining, reset } = await limit.limit(ip);
setResponseHeader(event, 'X-RateLimit-Limit', String(max));
setResponseHeader(event, 'X-RateLimit-Remaining', String(remaining));
setResponseHeader(event, 'X-RateLimit-Reset', String(reset));
if (!success) {
setResponseStatus(event, 429);
return { error: 'Rate limit exceeded' };
}
});
Per-user keying
For authenticated routes, prefer user ID over IP:
const session = await getUserSession(event); // your auth helper
const key = session?.userId || ip;
const result = await limit.limit(key);
Costs
Upstash free tier: 10K commands/day. Each rate-limit check is ~3 commands. Free tier covers ~3.3K rate-limited requests/day. Upgrade to paid ($0.20 per 100K commands) when you outgrow it.
What this doesn't solve
Same as any rate limiter: distributed attacks, bot traffic that mimics legitimate users, application-level abuse. Layer with the SecureNow firewall for IP-reputation blocking.
Related
Frequently Asked Questions
Is there a Nuxt module for rate limiting?
Not a first-party one. The community has nuxt-rate-limit but it's not actively maintained as of 2026. Hand-rolling on top of Upstash Redis is the more reliable path.
Does this work on Vercel and self-host?
Yes — Upstash Redis is HTTP-accessible from both. The same code runs in either deployment.
How do I scope to specific routes?
Filter on `getRequestURL(event).pathname` inside the middleware. Apply different limits per pattern.
Recommended reading
Block one IP, a CIDR range, or a single route and method in a Node.js app within seconds, with no code change: setup, scoping, CLI, MCP, verification and undo.
Oct 27Built your app with a coding agent? 11 warning signs it needs a security audit, each checkable in five minutes, and how to run the full audit yourself.
Oct 26Part 1 of the AI security audit series: set up an AI coding agent to audit your app locally, connect SecureNow, and run your first threat-model pass.
Jul 24