CSRF protection
Protect forms with the XSRF-TOKEN cookie and the X-XSRF-TOKEN header.
The Inertia client reads the XSRF-TOKEN cookie and sends its value as the X-XSRF-TOKEN header on every request. UseInertiaAntiforgery() issues that cookie and validates it, so one call protects every form.
What it does
- Issue. Page loads (GET/HEAD requests that are Inertia requests or accept
text/html) get the request token in a script-readable, essential cookie (HttpOnlyoff,SameSite=Lax,Path=/,SecureperSecurePolicy). Essential means a cookie-consent policy does not withhold it. - Validate. Every unsafe request (anything but GET, HEAD, OPTIONS and TRACE), with a JSON or a form body, Inertia or not, is checked with
IAntiforgery.ValidateRequestAsync. A plain cross-site HTML form post is rejected as well as a forged XHR. It also sets the antiforgery feature, so form-binding endpoints work withoutapp.UseAntiforgery(). - Reject. A missing or wrong token gets
419(page expired, as in Laravel); the handler does not run. - Opt out per endpoint with
.DisableAntiforgery()(webhooks, test hooks), or for whole areas withShouldValidate.
Options
OnRejected runs after UseInertia(); returning Back() gives the tab a fresh token and a message instead of the client's error dialog.
Lazy refresh
By default every page load issues a fresh token, and antiforgery marks that response Cache-Control: no-cache, no-store (which also keeps the page out of the browser's back/forward cache).
o.CookieRefresh = XsrfCookieRefresh.Lazy keeps a token that still validates. Such responses get no Set-Cookie and stay cacheable, and a token issued before a sign-in or sign-out is replaced on the next page load.
Validating costs about what issuing does, so the gain is in the responses, not in CPU. Set your own Cache-Control on pages with private data.
Session changes
A session that expires or a sign-in that changes the user invalidates the token in an open tab; the next page load issues a new one. Returning Back() from OnRejected gives that tab a fresh token and a message.
MVC and FastEndpoints
UseInertiaAntiforgery() covers controllers too. [AutoValidateAntiforgeryToken] as a global filter still works, but is not needed. FastEndpoints is covered by the middleware; FastEndpoints' own EnableAntiforgery() is not needed for Inertia forms.
Issue only
UseInertiaAntiforgeryCookie(cookieName = "XSRF-TOKEN") (the 1.0 API) only writes the cookie. You then validate with app.UseAntiforgery() (form bodies only) and your own check for JSON bodies (await antiforgery.ValidateRequestAsync(context)). Prefer UseInertiaAntiforgery().
Troubleshooting 419
- Was the page loaded through the middleware? It sets the cookie.
- Does the cookie reach the browser?
Securecookies are dropped on plain HTTP; behind a TLS-terminating proxy, configure forwarded headers or setCookieSecurePolicy.Always. - Do all instances share the Data Protection key ring?
- Does
UseAuthentication()run before the antiforgery middleware? - Did a sign-in or sign-out change the token? Reload the page, or use
OnRejected.
See Deploying for proxy and multi-instance settings.