Next.js cookbook · Intermediate
Rate limit Route Handlers and Server Actions
Protect login, signup and AI endpoints from abuse in a serverless deployment.
Approach
Serverless functions don't share memory, so use a shared store. @upstash/ratelimit with Upstash Redis (HTTP-based, works at the edge) provides sliding-window and token-bucket limiters. Key by user ID or IP, and return 429 with Retry-After.
Solution
// lib/ratelimit.ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
export const loginLimiter = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, "15 m"),
prefix: "rl:login",
analytics: true,
});
// app/api/auth/login/route.ts
import { headers } from "next/headers";
import { loginLimiter } from "@/lib/ratelimit";
export async function POST(request: Request) {
const ip = (await headers()).get("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown";
const { success, reset, remaining } = await loginLimiter.limit(ip);
if (!success) {
return Response.json({ title: "Too many attempts" }, {
status: 429,
headers: { "Retry-After": String(Math.ceil((reset - Date.now()) / 1000)), "RateLimit-Remaining": String(remaining) },
});
}
// ... verify credentials
}
Pitfalls
- In-memory counters in serverless (reset per instance).
- Trusting
x-forwarded-forwhen not behind a trusted proxy (on Vercel it's set by the platform). - Rate limiting only the UI path while the action/API remains unprotected.
Variations
- Platform firewalls (Vercel WAF, Cloudflare) for broad bot/rate protection.
- Self-hosted Redis with a Lua-based limiter on Node runtimes.
Further reading: Upstash Ratelimit
Reference: https://upstash.com/docs/redis/sdks/ratelimit-ts/overview
More Next.js recipes
- Add a Content Security Policy with nonces in Next.js
- Add dark mode to Next.js without a flash
- Add SEO metadata, Open Graph and sitemaps
- Avoid data-fetching waterfalls in Server Components
- Build a validated JSON API with Route Handlers
- Create encrypted session cookies in Next.js
- Handle errors and 404s in the App Router
- Handle forms with Server Actions and validation