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?
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.

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.
'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.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
serverActions: {
allowedOrigins: ['app.example.com'],
},
},
}
export default nextConfigVerify the exact syntax against the installed version. Do not add * or every preview domain. Keep the allowlist limited to the exact origins your deployment serves.
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.
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.

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.
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.
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.
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.
Authentication Providers, OAuth, APIs, and Webhooks
Each authentication provider handles CSRF differently, so 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.
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.
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/action | Type | Authentication | Authorization | CSRF consideration |
|---|---|---|---|---|
No use server action found | Server Action | Not present | Not present | No implementation to review |
No route.ts mutation found | Route Handler | Not present | Not present | No implementation to review |
/send-mail.php | PHP POST contact handler | Public form | Not applicable to user-owned data | Separate publishing-site abuse review; not a Next.js example |
| No webhook route found | Webhook | Not present | Not present | No 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.
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=Nonewithout 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
allowedOriginsentries. - 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.


