Open-Source Wikis

/

Next.js

/

Systems

/

Server actions

vercel/next.js

Server actions

Server actions are App-Router-only mutating endpoints declared with 'use server'. They can be invoked as form actions or as direct function calls from client components. The framework auto-encrypts arguments (when they cross the network), generates the action endpoint URLs, and routes posts back through the renderer.

Source: packages/next/src/server/app-render/action-handler.ts (51 KB).

End-to-end flow

sequenceDiagram
    participant Browser
    participant Client as Client component
    participant Encrypt as encryption.ts
    participant Server as action-handler.ts
    participant Action as User action

    Client->>Browser: await myAction(arg)
    Browser->>Server: POST<br/>Next-Action: <id><br/>body: encoded args
    Server->>Encrypt: decode + decrypt args
    Encrypt-->>Server: arg list
    Server->>Action: invoke
    Action-->>Server: return value or rerender
    Server->>Browser: Flight payload<br/>(action result + rerender)
    Browser->>Client: resolve promise

Compilation

When the user writes 'use server' in a function, the SWC transform at crates/next-custom-transforms/ rewrites it:

  • The function gets a stable, hashed action ID.
  • The body is moved to a server-only module so client bundles don't contain it.
  • The client side gets a stub that POSTs to the same page URL with Next-Action: <id> header.

Server actions inventory is recorded in server-reference-manifest.json so the runtime can look up the action by ID.

Argument encryption

packages/next/src/server/app-render/encryption.ts (12 KB) handles the symmetric encryption of bound arguments. When a server action references closed-over variables that need to flow to the client and back, those values are AES-encrypted with a per-build key. This prevents tampering — a malicious client can't substitute different argument values in transit.

The key is derived from NEXT_SERVER_ACTIONS_ENCRYPTION_KEY (env var) or auto-generated and stored at build time. Encryption happens through encryption-utils.ts and encryption-utils-server.ts.

CSRF protection

packages/next/src/server/app-render/csrf-protection.ts (3 KB) enforces same-origin checks on action requests:

  • The Origin header must match the request Host.
  • For action POSTs, the Content-Type must be one of application/x-www-form-urlencoded, multipart/form-data, or text/plain (form-compatible MIMEs that browsers send by default for cross-origin form posts).

validateRSCRequestHeaders was just enabled by default in commit 4ba05cc300 ("enable validateRSCRequestHeaders by default") — additional header validation that further locks down RSC endpoints.

Action handler

action-handler.ts is the server-side dispatcher. It:

  1. Reads the Next-Action header from the request.
  2. Looks up the action handler in server-reference-manifest.json.
  3. Reads and parses the request body (form data, JSON, or React's encoded format).
  4. Decrypts bound arguments via encryption.ts.
  5. Sets up the action async storage (action-async-storage.external.ts).
  6. Invokes the user's action.
  7. If the action returns a value, encodes it as a Flight payload.
  8. If the action calls revalidatePath, revalidateTag, or redirect, those signal the renderer to produce a new page response.

The handler is large because it covers form-data parsing, file uploads (via busboy), and integration with all of redirect, revalidation, and after-tasks.

Form integration

next/form (packages/next/src/client/form.tsx) is a thin wrapper around <form> that recognizes server-action action props and adds:

  • Optimistic navigation while the action runs.
  • Progressive enhancement: works without JavaScript, then enhances after hydration.

The form-shared.tsx file handles the shared logic between server and client form components.

Logging

packages/next/src/server/dev/server-action-logger.ts adds verbose logs in dev: each action invocation is logged with its name, duration, and outcome.

Integration points

  • Compiled by an SWC transform in crates/next-custom-transforms/.
  • Recorded in server-reference-manifest.json at build time.
  • Dispatched by action-handler.ts via the action async storage.
  • Encrypted args use a per-build key; CSRF check enforces same-origin.

Entry points for modification

  • To change action dispatch: packages/next/src/server/app-render/action-handler.ts.
  • To change CSRF rules: packages/next/src/server/app-render/csrf-protection.ts.
  • To change argument encryption: packages/next/src/server/app-render/encryption.ts.
  • To change form integration: packages/next/src/client/form.tsx and form-shared.tsx.

Key source files

File Purpose
packages/next/src/server/app-render/action-handler.ts Action dispatcher
packages/next/src/server/app-render/encryption.ts Argument encryption
packages/next/src/server/app-render/csrf-protection.ts Same-origin / CSRF enforcement
packages/next/src/server/app-render/action-async-storage.external.ts Action context per invocation
packages/next/src/client/form.tsx next/form component
packages/next/src/client/components/use-action-queue.ts Client-side queueing of in-flight actions
packages/next/src/client/components/unrecognized-action-error.ts Error for stale action IDs
crates/next-custom-transforms/ SWC transform that rewrites 'use server'

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.