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.

May 9, 2026·4 min read

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.

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 an IP, a Range or One Route Without a Deploy (Node.js)

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 27
11 Signs Your AI-Built App Needs a Security Audit

Built 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 26
AI Security Audit Part 1: Set Up Your Agent to Audit Code

Part 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