CSRF protection on Next.js Server Actions
Server Actions accept cross-origin POSTs by default. Add origin check or token validation.
Server Actions = cross-origin-by-default RPC. Without origin check, an attacker's site can trigger your app's actions on a logged-in user.
What it is
Server Actions are Next.js's RPC mechanism for invoking server code from client. By default they accept POST from any origin, making them CSRF-vulnerable.
Vulnerable example
// app/account/actions.ts
"use server";
export async function deleteAccount() {
const session = await auth();
if (!session) throw new Error("unauth");
await db.users.delete(session.user.id);
// Vulnerable: any cross-origin site can trigger this if user is logged in
}Fixed example
// app/account/actions.ts
"use server";
import { headers } from "next/headers";
async function requireSameOrigin() {
const h = await headers();
const origin = h.get("origin");
const host = h.get("host");
if (!origin || new URL(origin).host !== host) throw new Error("origin mismatch");
}
export async function deleteAccount() {
await requireSameOrigin();
const session = await auth();
if (!session) throw new Error("unauth");
await db.users.delete(session.user.id);
}How Securie catches it
apps/web/app/api/route.ts:22CSRF protection on Next.js Server Actions
CsrfSpecialist catches Server Actions without origin check at PR time. AuthAuthz specialist catches missing session check.
// app/account/actions.ts
"use server";
import { headers } from "next/headers";
async function requireSameOrigin() {
const h = await headers();
const origin = h.get("origin");
const host = h.get("host");
if (!origin || new URL(origin).host !== host) throw new Error("origin mismatch");
}
export async function deleteAccount() {
await requireSameOrigin();
const session = await auth();
if (!session) throw new Error("unauth");
await db.users.delete(session.user.id);
}Checklist
- Origin check at top of every state-changing Server Action
- Session check after origin check
- Re-MFA for high-risk actions (delete account, change password, change email, change payout)
- Audit-log every state-changing action
- Securie's CsrfSpecialist reviews every PR
FAQ
Does Next.js have built-in CSRF protection?
Limited. Modern Next.js checks Origin/Host alignment but only on Server Actions (not API routes). Add explicit checks.
What about Form actions?
Same pattern — use the requireSameOrigin helper.
Related guides
Server Actions are Next.js's RPC mechanism — async functions marked "use server" that run on the server but are called from client components. The convenience hides the risk: every Server Action is an unauthenticated public API endpoint by default. Here is the vulnerable pattern, the fix, and the audit checklist.
Server Actions execute server-side but default to no auth. Add session-required guard at the top of every protected action.
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.
NEXT_PUBLIC_-prefixed env vars ship in the client bundle. Server secrets accidentally prefixed = bundled credentials shipped to every visitor. Here's the detection + fix.