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.

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

AI Security Auditing, Part 1: Set Up Your AI Agent to Audit Your Code

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 24
How to Audit Your App's Security With AI: The 2026 Guide

A 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 24
AI Security Auditing, Part 2: Audit Authentication & Sessions With AI

Part 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