laravel/framework · warning · TokenMismatchException
CSRF token mismatch.
Error message
CSRF token mismatch.
What it means
PreventRequestForgery (the successor to VerifyCsrfToken) throws TokenMismatchException when none of its acceptance guards pass: running unit tests, the URI being in the except list, a valid Origin/Sec-Fetch-Site header, or a matching CSRF token. The exception renders as HTTP 419 by default. It exists to stop cross-site request forgery on state-changing verbs.
Source
Thrown at src/Illuminate/Foundation/Http/Middleware/PreventRequestForgery.php:111
* @throws \Illuminate\Http\Exceptions\OriginMismatchException
*/
public function handle($request, Closure $next)
{
if (
$this->isReading($request) ||
$this->runningUnitTests() ||
$this->inExceptArray($request) ||
$this->hasValidOrigin($request) ||
$this->tokensMatch($request)
) {
return tap($next($request), function ($response) use ($request) {
if ($this->shouldAddXsrfTokenCookie()) {
$this->addCookieToResponse($request, $response);
}
});
}
throw new TokenMismatchException('CSRF token mismatch.');
}
/**
* Determine if the HTTP request uses a ‘read’ verb.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function isReading($request)
{
return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}
/**
* Determine if the application is running unit tests.
*
* @return bool
*/View on GitHub (pinned to e0f6eb3518)
Solutions
- Ensure the SPA reads the XSRF-TOKEN cookie and sends it as X-XSRF-TOKEN header (axios does this automatically when on the same origin).
- Increase session.lifetime in config/session.php to outlast the longest realistic form idle.
- Add the route to PreventRequestForgery::$except (or the validateCsrfTokens / except paths) if it is genuinely exempt (e.g. a webhook with its own signature).
- Verify SESSION_DOMAIN and SESSION_SECURE_COOKIE match the deployment; mismatched cookie domain silently drops the XSRF cookie.
- For APIs, route them under routes/api.php with the Sanctum/Passport token guard so CSRF does not apply.
Example fix
// before: bare fetch, no token
fetch('/profile', { method: 'POST', body: formData });
// after: read XSRF cookie and send header
const token = document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1];
fetch('/profile', {
method: 'POST',
headers: { 'X-XSRF-TOKEN': decodeURIComponent(token), 'X-Requested-With': 'XMLHttpRequest' },
body: formData,
}); Defensive patterns
Strategy: validation
Validate before calling
// Frontend: confirm a fresh XSRF cookie exists before submitting
function hasFreshXsrf() {
return /XSRF-TOKEN=[^;]/.test(document.cookie);
}
if (!hasFreshXsrf()) { location.reload(); /* refresh session cookie */ } Try / catch
use Illuminate\Session\TokenMismatchException;
try {
$response = $httpClient->post('/profile', $form);
} catch (TokenMismatchException $e) {
// typically rendered as 419; re-fetch the page to refresh tokens
} Prevention
- Use axios with withCredentials on same-origin — it auto-attaches X-XSRF-TOKEN.
- Set session.lifetime generously relative to longest form idle.
- Keep SESSION_DOMAIN and SESSION_SECURE_COOKIE aligned with deployment.
- Route stateless API consumers under the api guard / Sanctum, not web.
When it happens
Trigger: A POST/PUT/PATCH/DELETE request reaches the middleware without a valid XSRF-TOKEN cookie + X-CSRF-TOKEN/_token header match, AND it is not reading-verb, not in the except array, not from the test runner, and does not satisfy hasValidOrigin(). Typically a form/AJAX submission whose token expired or was never attached.
Common situations: Session lifetime (lifetime in config/session.php) shorter than the time the user spent on a form. Frontend SPA forgot to read the XSRF-TOKEN cookie into axios/fetch headers. Subdomain or cross-origin POST where Sec-Fetch-Site is 'cross-site' or absent. Behind a proxy that strips cookies. Cookie domain misconfigured in session config.
Related errors
- Origin mismatch.
- Unauthenticated.
- The given password does not match the current password.
- Cookie jar has not been set.
- Callback must be a callable, callback array, or a 'Class@met
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/796147495727edb6.
Report an issue: GitHub.