Skip to main content
Security & Reliability · Blog 27

Next.js 16 CSRF Protection

Protect Server Actions, Route Handlers, cookie-authenticated forms, and production deployments without adding a redundant one-size-fits-all token system.

Secure browser form request passing through a layered shield while a hostile cross-site request is blocked

In Blog #26, we hardened browser-facing responses with security headers and Content Security Policy. Now we protect another boundary: state-changing requests. A CSRF attack tries to make an authenticated browser perform an action the user did not intend.

Next.js CSRF protection is architecture-specific. Current Next.js 16 documentation describes built-in request checks for Server Actions, but those checks do not turn 'use server' into authentication, authorization, or validation, and they must not be projected onto arbitrary Route Handlers.

What Is CSRF in Next.js?

Cross-Site Request Forgery is a browser attack in which an untrusted site causes a request to a trusted application where the victim is already authenticated. If the browser attaches a session cookie and the server accepts the mutation without checking intent or request context, the attacker may trigger an account change, purchase, transfer, invitation, or other action without learning the cookie value.

The relevant question is not simply “does this request use POST?” It is: what authenticates the request, which browser contexts send that credential, which framework checks apply to this endpoint, and which authorization rule protects the exact resource?

Diagram 1: CSRF attack and decision flowA signed-in browser can be induced to send a mutation; layered request checks decide whether the application processes or rejects it.
Victim signs inBrowser holds session cookieMalicious site triggers requestTrusted application checks context
Valid: processInvalid: reject
Diagram 2: The attacker does not need the cookie valueThe browser, not the attacker page, may attach an eligible cookie to the request; that automatic behavior creates the CSRF risk.
Trusted applicationSet session cookie after loginReceives authenticated mutation
Attacker siteCannot read an HttpOnly sessionAttempts to cause a browser request

Authentication, Authorization, CSRF, and Validation

These controls answer different questions. Authentication establishes who is calling. Authorization checks whether that identity may perform the operation on this account, tenant, or record. CSRF protection checks whether a cookie-authenticated browser request has acceptable same-origin intent or proof. Input validation checks the shape and business validity of submitted values. A serious mutation can need all four.

Diagram 3: Security layers for a mutationEvery accepted state change passes identity, permission, request-intent, and input checks.
RequestAuthenticateAuthorizeCSRF checkValidateState change
Browser request passing through origin, identity, permission and validation layers before reaching scaled application servers and shared state
No single gate replaces the others. Production requests need consistent checks across the proxy, application replicas, and data layer.

Does Next.js 16 Protect Against CSRF?

For Server Actions, yes—partly and specifically. The current Next.js data-security guide says Server Actions use POST and compare the request Origin with Host or X-Forwarded-Host, aborting when they do not match. The Server Actions configuration reference documents serverActions.allowedOrigins for legitimate additional origins.

That is not a universal CSRF middleware for every endpoint. A Route Handler is an explicit HTTP boundary whose cookie use, callers, methods, CORS behavior, and protections must be reviewed separately. Framework checks also do not prove that a user owns a record or may change a role.

Server Action

Uses the framework action transport and its documented checks. Still authenticate, authorize, and validate inside the action.

Route Handler

Review it as an independent HTTP endpoint. Do not assume Server Action Origin checks automatically apply.

Diagram 4: Secure Server Action flowFramework request checks precede application identity, permission, and data validation.
Client formServer Action POSTFramework request checksSession + authorizationValidated mutation
Educational Server Action boundary
'use server'

export async function updateProject(formData: FormData) {
  const session = await requireSession()
  const input = UpdateProjectSchema.parse(Object.fromEntries(formData))
  await requireProjectPermission(session.user.id, input.projectId, 'update')
  return updateProjectRecord(input)
}

The action deliberately performs authentication and resource-level authorization. The schema is not an authorization system, and a hidden projectId is untrusted input. Review the mutation architecture in Blog #7: Server Actions and secure form handling in Blog #18: Forms & Validation.

Server Action Origin Protection Behind a Reverse Proxy

A reverse proxy such as Traefik may terminate TLS and forward host information. Next.js uses Host or X-Forwarded-Host during the documented Server Action Origin comparison, so the proxy must set trusted forwarding headers predictably and prevent clients from smuggling arbitrary values. Fix the proxy’s canonical-host behavior instead of broadly allowing origins to silence errors.

Use only for justified additional origins
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    serverActions: {
      allowedOrigins: ['app.example.com'],
    },
  },
}

export default nextConfig

Verify the exact syntax against the installed version. Do not add * or every preview domain. The absence of a root package in this repository means no exact Next.js patch or real allowlist can be reported.

Diagram 5: Reverse proxy and Origin comparisonThe public browser origin, trusted forwarded host, and application expectation must agree.
Browser + OriginTraefik TLS boundaryTrusted forwarded hostNext.js comparisonContinue or reject
Diagram 6: Server Action versus Route HandlerThe framework action transport and a custom HTTP endpoint require separate security reviews.
Server ActionPOST-only action transportDocumented Origin/host checkAuth + authorization still required
Route HandlerExplicit HTTP contractReview cookies and callersSelect CSRF controls deliberately

Which Requests Need CSRF Protection?

Inventory every state-changing Server Action and every POST, PUT, PATCH, and DELETE handler that can use browser credentials. Account email and password changes, role updates, purchases, transfers, invitations, deletes, subscriptions, and admin commands deserve explicit review. Keep GET, HEAD, and OPTIONS free of side effects.

Diagram 7: HTTP method intentRead methods remain safe and repeatable; mutations use explicit methods and security checks.
Read-onlyGET profileHEAD metadataOPTIONS capabilities
State-changingPOST createPUT/PATCH updateDELETE remove

SameSite, Secure, and HttpOnly Cookies

Cookie-based sessions are an important CSRF context because browsers manage and attach cookies according to request and cookie rules. SameSite influences cross-site sending. Lax commonly preserves ordinary top-level safe-method navigation while limiting many cross-site mutation contexts. Strict is more restrictive and can disrupt legitimate cross-site arrival or authentication flows. None permits cross-site sending and requires Secure, demanding a stronger review of the application’s intended cross-site architecture.

HttpOnly reduces direct JavaScript access to a cookie; it does not stop the browser from sending it. Secure restricts cookie transport to secure contexts; it does not prove request intent. Domain, Path, prefix, and lifetime also influence scope and exposure. See Blog #13: Authentication for the complete session boundary.

Session cookie at a site boundary allowing a trusted request while blocking an untrusted cross-site request
SameSite changes when cookies travel. It is a valuable layer, not proof of authentication, authorization, or universal CSRF safety.
Diagram 8: SameSite behavior is contextualThe browser evaluates site relationship, navigation context, method, and the cookie’s SameSite setting.
Request context
same-originsame-sitecross-site
StrictLaxNone + Secure
Send or withhold cookie
Diagram 9: Cookie attributes solve different problemsTransport, script access, cross-site sending, host scope, path, and lifetime combine without replacing request authorization.
Secure: HTTPS transportHttpOnly: no direct JS readSameSite: cross-site rulesDomain: host scopePath: request scopeExpiry: session lifetime

When CSRF Tokens Are Appropriate

A token pattern adds proof that a trusted application context obtained a value an attacker cannot submit correctly. OWASP recommends using maintained framework protection first. For stateful applications that need a custom defense, a synchronizer token is stored in server-side session state and submitted separately. For stateless designs, OWASP recommends a signed double-submit cookie bound to session context rather than a naive equality-only cookie.

Tokens must be unpredictable, validated server-side, appropriately bound, compared safely, excluded from URLs, and never logged. A static token, predictable value, or cookie-only check is not a valid defense. Do not add token storage or Redis calls unless a real architecture needs them.

Diagram 10: Synchronizer token flowThe server issues a session-bound unpredictable value; the trusted form returns it and the server validates it before mutation.
Server sessionGenerate unpredictable tokenTrusted formSubmit separately
Match: continueWrong: reject
Diagram 11: Signed double-submit patternA cookie value and separately submitted value are verified with a session-bound signature, reducing naive cookie-injection risk.
Session contextSigned token cookieToken in form/headerVerify signature + relationshipAccept or reject

Origin, Referer, and Fetch Metadata

For cookie-authenticated mutations, comparing the request Origin with an exact trusted origin can be a useful defense. When Origin is absent, some architectures use a carefully parsed Referer fallback, but privacy behavior and legitimate clients mean it should not be treated as a universal solution. Compare parsed scheme, host, and port—not an unsafe substring—and understand which target host the proxy makes authoritative.

Fetch Metadata headers such as Sec-Fetch-Site, Sec-Fetch-Mode, and Sec-Fetch-Dest describe browser request context. OWASP documents policies that reject obvious cross-site unsafe requests while permitting legitimate same-origin traffic and handling absent headers with a fallback. Roll out with monitoring and test navigation, OAuth, embedded, and older-client behavior.

Diagram 12: Exact Origin validationThe server parses and compares the source origin with the configured canonical application origin.
State-changing requestParse OriginExact trusted-origin match
Yes: continueNo: reject
Diagram 13: Fetch Metadata policyRequest context can reject obvious cross-site mutations while retaining a documented fallback for absent headers.
Incoming requestSec-Fetch-Site
same-origin: allowsame-site: assess trustcross-site + unsafe: rejectabsent: fallback

Forms and JavaScript Fetch Requests

A secure form combines the chosen CSRF strategy with authentication, resource-level authorization, schema validation, bounded input, safe errors, and rate limiting where abuse or cost justifies it. Hidden fields are user-controlled and never prove ownership or price. JavaScript fetch calls need the same reasoning: JSON is not magic, and credentials, cookies, custom headers, CORS, and caller architecture all affect the result.

Some API designs require a custom request header that ordinary cross-site HTML forms cannot set. That can force a CORS preflight, but the server must maintain a narrow origin policy and reject simple-content-type alternatives if they would bypass the intended contract.

Educational same-origin fetch shape
await fetch('/api/account/email', {
  method: 'PATCH',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': tokenFromReviewedArchitecture,
  },
  body: JSON.stringify({ email }),
})

CORS, CSP, XSS, and CSRF Are Different

CORS is not CSRF protection. It controls whether browser JavaScript from an origin may access a cross-origin response and governs certain non-simple requests. A state-changing request may still be caused even when an attacker cannot read the response.

CSP is not CSRF protection. The policy from Blog #26 restricts browser resources and script execution. It is valuable defense in depth but does not replace mutation checks. Likewise, CSRF controls do not replace XSS prevention: script executing in the trusted origin may act with the user’s privileges and include application-accessible proof.

Diagram 14: CORS and CSRF answer different questionsResponse-sharing policy and unwanted authenticated mutation are related browser concerns, not interchangeable protections.
CORSMay this origin’s JavaScript read the response?Controls cross-origin browser access rules
CSRFDid the user intend this authenticated state change?Requires mutation-specific defenses
Diagram 15: Security responsibilities remain layeredOutput safety and CSP reduce XSS, SameSite/Origin/tokens address CSRF, and server authorization remains mandatory.
XSS defensessafe outputCSP
CSRF defensesSameSiteOrigintokens where needed
Authorizationidentitytenant/ownershipoperation permission

Authentication Providers, OAuth, APIs, and Webhooks

No Auth.js, Clerk, or other Next.js authentication provider is present in this publishing repository, so there is no provider behavior to claim. In a real application, follow the provider’s current documentation and reuse its session, OAuth state, PKCE, callback, and CSRF controls. OAuth state protects a login transaction; it is not a universal token for every application form.

Bearer-token APIs have different CSRF characteristics because browsers do not automatically invent and attach an arbitrary Authorization header as they do eligible cookies. Token storage and XSS exposure then become central risks. Webhooks are different again: they usually authenticate the provider with a signature over the raw body, then enforce timestamp, replay, and idempotency rules. Do not apply browser-form tokens blindly to POST /api/webhook. Review custom HTTP boundaries in Blog #10: Route Handlers.

Diagram 16: Browser form versus provider webhookCookie-authenticated user mutations and signed server-to-server events use different trust models.
Browser formUser session cookieCSRF considerationsUser authorization
Provider webhookRaw signed bodyProvider signatureReplay + idempotency checks

Multi-Instance State, Logging, and Rate Limits

Across App A, B, and C, every replica needs the same canonical-origin rules. If a synchronizer-token design stores required state, the next replica must be able to validate it through a shared session store or a consistent cryptographic design. Do not keep required state only in one container’s memory. Use the scaling principles from Blog #25; do not add Redis solely because this article mentions CSRF.

Log a safe event for rejected origins, invalid proof, suspicious cross-site mutation attempts, and repeated validation failure. Never record session cookies, authorization headers, raw CSRF tokens, passwords, webhook secrets, or sensitive form bodies. Rate limiting from Blog #22 can constrain abuse, but does not establish request intent. Connect safe events to the practices in Blog #24.

Diagram 17: Multi-instance CSRF stateWhen the selected pattern requires server state, every replica validates against a consistent shared session or token source.
App AApp BApp C
Shared session or verification stateConsistent decision
Diagram 18: Safe CSRF monitoringA rejected request becomes a minimal redacted event for aggregation and investigation—not a token dump.
Invalid requestRejectRedacted security eventLogs + monitoringInvestigate abnormal patterns

Practical Project Review

The repository search found no actual Next.js mutation surface to classify. The table reports discovered evidence without inventing application routes.

Endpoint/actionTypeAuthenticationAuthorizationCSRF consideration
No use server action foundServer ActionNot presentNot presentNo implementation to review
No route.ts mutation foundRoute HandlerNot presentNot presentNo implementation to review
/send-mail.phpPHP POST contact handlerPublic formNot applicable to user-owned dataSeparate publishing-site abuse review; not a Next.js example
No webhook route foundWebhookNot presentNot presentNo provider signature flow to verify

A safe implementation order is: inventory mutations; verify authentication and authorization; inspect cookie attributes; confirm installed Next.js Server Action behavior; review Route Handlers separately; map Origin and trusted proxy behavior; decide whether tokens are necessary; reuse provider or framework controls; add only justified protection; test accepted and rejected paths; inspect redacted logs; then run the real project’s build and tests.

How to Test CSRF Protection

Test only an application you own or are authorized to assess. In a local or isolated environment, exercise the legitimate same-origin request, missing or invalid proof where applicable, wrong and missing Origin behavior, cross-site simulation, SameSite cookie behavior, unauthenticated and unauthorized callers, and the production proxy path. Use synthetic credentials—never paste real production sessions into commands, fixtures, screenshots, or logs.

Diagram 19: CSRF test matrixSuccess requires both legitimate acceptance and reliable rejection of invalid or unauthorized mutations, with secret-free logs.
Legitimate requestsame originauthorized userShould succeed
Invalid CSRF requestwrong origin/proofcookie may existShould reject
Unauthorized requestvalid formatwrong permissionShould reject

Common Next.js CSRF Mistakes

  • Assuming Server Actions remove all security work.
  • Projecting Server Action checks onto Route Handlers.
  • Changing data through GET requests.
  • Using SameSite=None without reviewing cross-site behavior.
  • Claiming HttpOnly or Secure prevents CSRF.
  • Treating CORS or CSP as a CSRF substitute.
  • Using a static, predictable, or URL-based token.
  • Logging tokens, cookies, or sensitive form bodies.
  • Validating a token without authenticating the session.
  • Authenticating without resource-level authorization.
  • Trusting Origin without proxy awareness.
  • Adding broad allowedOrigins entries.
  • Rebuilding provider protections unnecessarily.
  • Treating webhooks like browser forms.
  • Keeping required state in one replica’s memory.
  • Ignoring XSS and client-side CSRF.

Next.js 16 CSRF Protection Checklist

  • Keep GET, HEAD, and OPTIONS read-only.
  • Authenticate every sensitive mutation.
  • Authorize the exact operation and resource.
  • Validate every client-controlled value.
  • Verify current Server Action behavior for the installed version.
  • Review cookie-authenticated Route Handlers independently.
  • Choose SameSite deliberately.
  • Review HttpOnly, Secure, Domain, Path, prefix, and lifetime.
  • Keep Server Action allowed origins narrow.
  • Define trusted proxy forwarding behavior.
  • Use provider-native auth and OAuth protections.
  • Use reviewed token patterns only where needed.
  • Never put CSRF proof in a URL or log.
  • Use Origin/Referer or Fetch Metadata according to the real compatibility plan.
  • Do not use CORS or CSP as substitutes.
  • Verify webhook signatures separately.
  • Share required validation state across replicas.
  • Rate-limit abuse without calling it CSRF protection.
  • Log only redacted security events.
  • Test critical accepted and denied paths before deployment.

Frequently Asked Questions

What is CSRF in Next.js?

Cross-Site Request Forgery is an attack in which another site causes a browser to send an unwanted authenticated state-changing request to a Next.js application. It matters most when authentication credentials, especially cookies, are attached automatically.

Does Next.js 16 automatically prevent CSRF?

Next.js 16 has built-in protections for Server Actions, including POST-only invocation and Origin comparison with Host or X-Forwarded-Host. Do not assume those controls protect arbitrary Route Handlers or replace authentication, authorization, and validation.

Are Next.js Server Actions protected from CSRF?

Current Next.js documentation describes Origin-to-host comparison and POST-only invocation as built-in Server Action protections. A sensitive action must still authenticate the caller, authorize the exact operation, validate input, and use a correctly configured proxy.

Do Server Actions still need authentication?

Yes. A Server Action is a remotely invokable server entry point. It must validate the current session before sensitive work.

Do Server Actions still need authorization?

Yes. Authentication identifies a caller; authorization decides whether that caller may perform this operation on this resource or tenant.

What is SameSite?

SameSite is a cookie attribute that influences when a browser includes a cookie in cross-site requests. Strict, Lax, and None have different security and compatibility trade-offs.

Should I use SameSite=Lax or Strict?

It depends on the application flow. Strict is more restrictive but may interrupt legitimate cross-site arrivals. Lax often balances navigation and protection, but it is not universally sufficient.

Does HttpOnly prevent CSRF?

No. HttpOnly limits direct JavaScript access to a cookie, but the browser can still attach that cookie to requests.

Does Secure prevent CSRF?

No. Secure limits a cookie to secure transport contexts. It protects transport, not the intent or origin of a state-changing request.

Do I need a CSRF token in Next.js?

It depends on the exact flow, framework behavior, authentication provider, cookie policy, callers, and threat model. Prefer maintained built-in protection; add a reviewed token pattern only where the architecture requires it.

What is Origin validation?

The server compares the request Origin with the application origin it expects. Proxy forwarding and canonical host configuration must be correct, and allowlists should remain narrow.

Is CORS enough to prevent CSRF?

No. CORS controls cross-origin browser access to responses and certain requests; CSRF concerns an unwanted authenticated state change. They are different controls.

Does CSP prevent CSRF?

No. CSP is valuable defense in depth for browser content and script execution, but it does not replace request-level CSRF defenses or authorization.

Are Route Handlers protected like Server Actions?

Do not assume so. Review every cookie-authenticated POST, PUT, PATCH, and DELETE Route Handler as an independent HTTP endpoint and choose protection for its actual callers.

Should GET requests change data?

No. Keep GET, HEAD, and OPTIONS read-only. State changes belong behind mutation methods with authentication, authorization, validation, and the appropriate CSRF strategy.

How should webhooks be protected?

A provider webhook normally uses signature verification, a provider secret, and replay controls rather than browser CSRF tokens. Preserve the signed raw body and follow current provider documentation.

How does CSRF work with multiple Next.js containers?

Origin and proxy rules must be consistent across replicas. If a chosen token design uses server-side state, that state must be shared or otherwise available to every replica that may receive the next request.

Can XSS bypass CSRF protections?

Malicious JavaScript running in the trusted origin can often act with the user privileges and include values available to the application. CSRF controls therefore do not replace XSS prevention, CSP, or output safety.

Current Official References

Next Steps

Begin with the real application inventory, not a package installation. Separate Server Actions from Route Handlers, document cookies and callers, align Origin and proxy behavior, preserve authentication and authorization, and add a token mechanism only when the selected architecture needs one.

WhatsApp