HomeGuidesNext.js Server Actions security

Guide

Next.js Server Actions security

Server Actions are public HTTP endpoints. How to add authentication, authorization, and input validation so AI generated actions are safe to ship.

2 min read

Independent notes from VibeLocked. Not a pentest, and not affiliated with the tools named below.

Server Actions let you call server code from a form or button with one function. That convenience hides an important fact: every action is reachable over HTTP by anyone, not just by your UI. AI tools often generate actions that update a record by id without checking who owns it.

Checked against the Next.js docs linked below on October 13, 2026.

The rule: treat every action like a public API

For each "use server" function, ask three questions:

  1. Who is calling? Read the session and reject anonymous users where needed.
  2. Are they allowed? Check that the user owns the record or has the right role.
  3. Is the input valid? Parse arguments with a schema.
'use server';
import { z } from 'zod';
import { auth } from '@/lib/auth';
import { db } from '@/lib/db';

const Input = z.object({ postId: z.string().uuid(), title: z.string().min(1).max(200) });

export async function updatePost(raw: unknown) {
  const session = await auth();
  if (!session?.user) throw new Error('Unauthorized');

  const { postId, title } = Input.parse(raw);

  const post = await db.post.findUnique({ where: { id: postId } });
  if (!post || post.authorId !== session.user.id) throw new Error('Forbidden');

  await db.post.update({ where: { id: postId }, data: { title } });
}

The names auth and db are placeholders for your own auth library and database client.

Common mistakes to look for

  • Trusting a hidden field. A userId sent from the form can be changed by the caller. Read the user from the session instead.
  • Only hiding the button. Not rendering a delete button for non admins does not stop a direct request to the action.
  • Returning too much. Whatever an action returns is sent to the client. Return only the fields the UI needs.
  • Auth only in middleware. Keep checks in the action or in a shared data access layer.
  • Secrets in client components. Server only code can use secrets. Mark modules with import 'server-only' so they fail to build if imported into the client.

Built in protections, and their limits

Next.js only accepts POST for actions and compares the Origin header to the host, which helps against CSRF. It also creates encrypted, non deterministic action IDs and removes unused actions from the client bundle. None of that replaces your own authentication and authorization checks.

If your app runs behind a proxy with a different domain, review the serverActions.allowedOrigins option.

Ask your AI tool to audit

Paste each action into your AI assistant with the security review prompt, and ask specifically: "Can any user call this with another user's id?"

Official docs

Questions

Are Server Actions private because they run on the server?

No. Next.js docs say to treat Server Actions like public API endpoints. Anyone can send a POST request to them, so each action must check who is calling and whether they are allowed.

Is checking auth in middleware enough?

Do not rely on it alone. Next.js recommends verifying authentication and authorization close to the data, inside the action or a data access layer.

Do I need to validate arguments if TypeScript types them?

Yes. Types are not checked at runtime. Validate inputs with a schema library such as Zod before using them.