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
Part 1 of the AI Security Auditing series. Set up an AI coding agent to audit your application's security locally — install SecureNow, connect your account, and run your first threat-model pass in minutes.
Jul 24A practical, end-to-end guide to auditing your application's security with an AI coding agent — find vulnerabilities at the code level, build detections at the runtime level, and never upload your codebase to do it.
Jul 24Part 2 of the AI Security Auditing series. Use an AI agent to audit your login, session, MFA, password-reset and magic-link flows for account-takeover paths — then catch the attempts in live traffic.
Jul 23