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
SecureNow 8.1 adds `securenow alerts rules create` — define your own detection rules from SQL, scope them to your apps, and ship them without leaving the terminal. Here's how, with a real magic-link brute-force example.
Jun 11A copy-paste prompt that lets an AI coding agent install SecureNow, wire Next.js instrumentation, verify traces and logs, deploy to AWS, simulate attacks, and prove firewall blocking with human approval gates.
May 18
If your team uses Sentry for frontend errors and needs backend distributed tracing without doubling the Sentry bill, here's the OpenTelemetry path that doesn't make you choose.
May 9