CSRF Protection
Cross-site request forgery tricks a logged-in browser into submitting a request the user did not intend. Laravel's web middleware group rejects state-changing requests that lack a valid token.
How the token works
The application stores a random token in the session and includes it in forms. The verify middleware compares the submitted value with the session value. A malicious site cannot read the token because of the same-origin policy.
<form method="POST" action="/posts">
@csrf
...
</form>
@csrf renders a hidden _token field. The middleware accepts the token from the request body or from the X-CSRF-TOKEN header.
Tokens in JavaScript
<meta name="csrf-token" content="{{ csrf_token() }}">
await fetch('/posts', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Accept': 'application/json',
},
body: new FormData(form),
});
Some front-end setups use the X-XSRF-TOKEN cookie. Laravel encrypts that cookie, and the JavaScript library reads and sends it automatically. Choose one approach per application and apply it consistently.
Which requests are protected
The web group verifies POST, PUT, PATCH, and DELETE. GET, HEAD, and OPTIONS are considered safe and are not blocked. This is why state changes must never happen on GET routes.
Exceptions
Webhook endpoints receive requests from third parties that cannot know your token. Exclude only the specific paths, and verify the provider's signature inside the controller.
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'webhooks/stripe',
]);
})
SPAs and APIs
If the front end runs on the same domain and uses cookie authentication, CSRF protection still applies. Use Sanctum's SPA authentication and send the XSRF cookie. Token-based API clients that do not rely on cookies are not vulnerable to CSRF, so stateless API routes usually skip the middleware.
Session and token lifecycles
Regenerating the session invalidates the old token; include the current token after login. A page left open for a long time may expire with the session, producing a 419 response. Handle 419 in the UI with a reload prompt rather than silently retrying.
Checklist
- Include
@csrfin every state-changing form. - Send the token with Ajax writes.
- Never perform writes on GET routes.
- Exempt webhooks narrowly and verify signatures.
- Show a useful message for 419 errors.
Back to course overview · Previous: Contracts · Next: Authentication