CSRF protection in Next.js — when you need it, when you don't
Next.js App Router apps often skip CSRF protection because they think SameSite cookies are enough. They are not always enough. Here is the specific rule.
CSRF (Cross-Site Request Forgery) lets an attacker trigger a state-changing request in your app from a different origin while the victim is logged in. Modern browsers default to SameSite=lax cookies, which blocks most CSRF — but not all.
What it is
If your app authenticates via cookies and a state-changing action can be triggered by a GET or by a simple-content-type POST from a different origin, you are vulnerable. CSRF tokens verify the request originated from your own form.
Vulnerable example
// Vulnerable: any origin can POST a form that changes user email.
export async function POST(req: Request) {
const form = await req.formData();
const email = form.get("email") as string;
const session = await getSession(); // cookie-based
await prisma.user.update({ where: { id: session.userId }, data: { email } });
return Response.redirect("/settings");
}Fixed example
// Fixed: verify a double-submit CSRF token
import { cookies, headers } from "next/headers";
export async function POST(req: Request) {
const form = await req.formData();
const fromForm = form.get("csrf_token");
const fromCookie = (await cookies()).get("csrf_token")?.value;
if (!fromForm || fromForm !== fromCookie) {
return new Response("csrf", { status: 403 });
}
// ...continue
}How Securie catches it
apps/web/app/api/route.ts:22CSRF protection in Next.js
Securie's CSRF specialist maps every state-changing handler against its cookie-auth surface and requires an explicit CSRF defense — either a verified origin check, a token, or a non-cookie auth scheme.
// Fixed: verify a double-submit CSRF token
import { cookies, headers } from "next/headers";
export async function POST(req: Request) {
const form = await req.formData();
const fromForm = form.get("csrf_token");
const fromCookie = (await cookies()).get("csrf_token")?.value;
if (!fromForm || fromForm !== fromCookie) {
return new Response("csrf", { status: 403 });
}
// ...continue
}Checklist
- State-changing routes (POST/PUT/PATCH/DELETE) have CSRF defense if auth is cookie-based
- Header-based auth (Bearer token in Authorization) does not need CSRF tokens for same-reason protection
- SameSite=lax is the baseline; pair with an origin/referer check for sensitive routes
- GET requests never change state
- Form actions in Server Components use a signed token
FAQ
Do I need CSRF tokens if I use NextAuth?
NextAuth has built-in CSRF protection. Your own endpoints outside NextAuth still need their own defense.
Related guides
Misconfigured cookies are how session tokens leak. Here is exactly which flags to set for session, CSRF, and preference cookies in a Next.js app.
BOLA is the top item on the OWASP API Security Top 10 for a reason — every AI coding assistant introduces it by default. Learn what it looks like in Next.js, how to exploit it, and how to fix it.
Every week founders tweet about their OpenAI bill going from $10 to $10,000 overnight. Usually the cause is an API key committed to a public repo. Here is why it happens in Next.js specifically and how to stop it in five minutes.
Unlimited API endpoints are how $150K OpenAI bills happen. Here is how to add proper rate limiting to a Next.js app using Vercel Edge Middleware, Upstash, or your existing Redis.