Sitemap
Ankit Khoiwal

Ankit Khoiwal

beyond the code, a story, I publish in-depth developer guides & roadmaps at https://www.blogs-world.in/

Laravel Sanctum 419 Error: CORS & CSRF Fix (Laravel 13)

7 min readSep 8, 2026

--

Press enter or click to view image in full size

Fix 419 Page Expired, CSRF token mismatch, missing cookies, and broken SPA authentication in Laravel Sanctum with React, Vue, Vite, or another frontend.

If Laravel Sanctum returns a 419 Page Expired or CSRF token mismatch during SPA login, the problem is usually not one bad CORS setting.

Sanctum’s cookie-based authentication requires several pieces to agree:

  1. Your frontend must be treated as a stateful Sanctum domain.
  2. Laravel must enable stateful API middleware.
  3. CORS must allow credentials from the exact frontend origin.
  4. The session cookie must be valid for the frontend/API domain structure.
  5. Your frontend must send cookies and the XSRF token correctly.

A mistake in any one of these can turn a perfectly normal login request into a 419 error.

Here is the shortest reliable way to diagnose it.

What Does Laravel Sanctum 419 Mean?

A Laravel 419 response usually means CSRF validation failed.

For Sanctum SPA authentication, Laravel expects the browser to obtain a CSRF cookie before sending the login request.

The normal flow is:

GET /sanctum/csrf-cookie
↓
Laravel sets XSRF-TOKEN + session cookie
↓
POST /login
↓
Browser sends cookies + X-XSRF-TOKEN
↓
Laravel validates CSRF and creates authenticated session

If the cookie never arrives, is not returned to Laravel, or the CSRF header does not match it, the login request may return 419.

This is why randomly changing allowed_origins often does not solve the actual problem.

First: Identify Which Request Is Failing

Open DevTools → Network before changing your configuration.

Look at the first request that fails.

SymptomLikely problemOPTIONS request blockedCORS configuration/sanctum/csrf-cookie works but /login returns 419CSRF/session cookie issueLogin succeeds but /api/user returns 401Session/stateful-domain issueWorks in Postman but not browserBrowser CORS/cookie rulesWorks locally but fails in productionDomain, HTTPS or environment configLogin returns 422Validation problem, not CSRF

That distinction can save a lot of debugging time.

1. Enable Sanctum’s Stateful API Middleware

For current Laravel applications, configure Sanctum’s stateful API handling through bootstrap/app.php.

use Illuminate\Foundation\Configuration\Middleware;
->withMiddleware(function (Middleware $middleware): void {
$middleware->statefulApi();
})

A common problem is following an old tutorial that tells you to modify app/Http/Kernel.php.

With newer Laravel application structure, that may no longer be the correct place to configure this behavior.

Without stateful API handling, Laravel may treat a request from your SPA as stateless even when the browser has a valid session cookie.

2. Configure SANCTUM_STATEFUL_DOMAINS Correctly

Suppose your application uses:

Frontend:
https://app.example.com
Laravel API:
https://api.example.com

Your frontend belongs in Sanctum’s stateful domain configuration.

For example:

SANCTUM_STATEFUL_DOMAINS=app.example.com

Notice that this is a host, not a full URL with https://.

For local development, the port is important.

If Vite runs at:

http://localhost:5173

then your configuration may need:

SANCTUM_STATEFUL_DOMAINS=localhost:5173

Do not assume these are identical:

localhost
localhost:5173
127.0.0.1
127.0.0.1:5173

From the browser and Sanctum’s perspective, those differences matter.

3. Fix Laravel CORS for Credentialed Requests

Sanctum SPA authentication relies on cookies.

That means your CORS configuration must support credentialed browser requests.

A typical configuration looks similar to:

return [
    'paths' => [
'api/*',
'sanctum/csrf-cookie',
'login',
'logout',
],
'allowed_methods' => ['*'], 'allowed_origins' => [
'https://app.example.com',
],
'allowed_headers' => ['*'], 'supports_credentials' => true,];

The important part is:

'supports_credentials' => true,

And for credentialed requests, avoid using:

'allowed_origins' => ['*']

Use the actual frontend origin instead.

For example:

https://app.example.com

CORS is based on an origin, so the scheme matters here.

That means these are different:

http://app.example.com
https://app.example.com

This is different from SANCTUM_STATEFUL_DOMAINS, where you normally configure the frontend host.

4. Configure the Session Cookie Domain

If your frontend and API are sibling subdomains:

app.example.com
api.example.com

your Laravel session configuration typically needs to support the parent domain.

For example:

SESSION_DOMAIN=.example.com
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax

The leading dot allows the session cookie to apply across the required subdomains.

An important point is often missed here:

Cross-origin does not automatically mean cross-site.

app.example.com and api.example.com are different origins, but they normally belong to the same site.

So changing everything to:

SESSION_SAME_SITE=none

is not automatically the correct solution.

SameSite=None introduces additional requirements, including secure cookies, and can create another problem instead of fixing the original one.

5. Configure Axios to Send Cookies and XSRF

Your frontend also has to participate correctly.

A clean Axios client might look like this:

import axios from 'axios';
export const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
withCredentials: true,
withXSRFToken: true,
headers: {
Accept: 'application/json',
},
});

Then initialize CSRF protection before login:

await api.get('/sanctum/csrf-cookie');
await api.post('/login', {
email,
password,
});

Two settings are especially important:

withCredentials: true
withXSRFToken: true

withCredentials allows the browser to send credentials during cross-origin requests.

Get Ankit Khoiwal’s stories in your inbox

Join Medium for free to get updates from this writer.

withXSRFToken helps Axios send the XSRF token correctly.

After /sanctum/csrf-cookie, inspect your browser cookies.

You should normally see an XSRF-TOKEN and a Laravel session cookie.

Then inspect the login request and verify that the expected cookies and CSRF information are actually being sent.

Why Sanctum Works in Postman but Fails in the Browser

This is one of the most useful clues.

Postman does not enforce browser CORS behavior in the same way a web page does.

So this situation:

Postman → works
Browser → fails

often points toward:

  • CORS
  • cookies
  • credentials
  • CSRF handling
  • origin configuration

rather than a broken Laravel controller or login credentials.

If the browser’s OPTIONS request fails, fix CORS first.

If /sanctum/csrf-cookie succeeds but /login returns 419, inspect the cookies and X-XSRF-TOKEN.

Your SPA and API Domain Architecture Matters

Sanctum’s first-party SPA authentication is designed for a frontend and API that share the same top-level domain.

This architecture is appropriate:

app.example.com
api.example.com

This architecture is very different:

my-project.vercel.app
api.example.com

Adding both URLs to CORS does not magically make them the same first-party site.

CORS only determines whether the browser allows one origin to communicate with another.

It does not change browser cookie architecture.

If your frontend and API live on unrelated domains, consider:

  • moving the frontend to something such as app.example.com
  • using an intentional token-based authentication flow
  • using a backend-for-frontend architecture

Trying to solve unrelated-domain cookie authentication by adding more CORS wildcards usually creates more debugging problems.

Laravel Sanctum 419 Quick Checklist

If you only have two minutes, check these in order:

Laravel

✓ Stateful API middleware enabled
✓ Frontend included in SANCTUM_STATEFUL_DOMAINS
✓ Correct SESSION_DOMAIN
✓ HTTPS cookie settings correct
✓ Config/cache refreshed after environment changes

CORS

✓ Exact frontend origin allowed
✓ supports_credentials = true
✓ sanctum/csrf-cookie path covered
✓ login/API paths covered

Frontend

✓ GET /sanctum/csrf-cookie happens before login
✓ withCredentials = true
✓ withXSRFToken = true
✓ Accept: application/json

Browser

✓ XSRF-TOKEN exists
✓ Session cookie exists
✓ Cookies are sent with POST /login
✓ X-XSRF-TOKEN is present when required
✓ OPTIONS request succeeds

If one of those checks fails, you have probably found the layer causing the 419.

Common Fixes That Usually Make Things Worse

Disabling CSRF

Do not remove CSRF protection from your normal SPA login route simply to make the 419 disappear.

That hides the authentication configuration problem instead of solving it.

Allowing Every CORS Origin

Do not replace your frontend origin with * and assume the application is fixed.

Credentialed browser requests have stricter origin requirements.

Setting SameSite=None Everywhere

SameSite=None is not a universal Sanctum fix.

Understand whether your deployment is actually cross-site before changing cookie policy.

Mixing localhost and 127.0.0.1

Pick one hostname during development and use it consistently.

For example:

localhost:5173
localhost:8000

is easier to reason about than mixing localhost on one service with 127.0.0.1 on another.

Frequently Asked Questions

Why does Laravel Sanctum return a 419 error?

A 419 response commonly means Laravel could not validate the CSRF token associated with the user’s session. In Sanctum SPA authentication, this often happens because the CSRF cookie, session cookie, stateful-domain configuration, CORS credentials, or frontend XSRF handling is incorrect.

Does CORS directly cause Laravel’s 419 error?

Not always directly. But incorrect CORS configuration can prevent cookies or credentialed requests from working correctly, which can eventually cause Laravel’s CSRF validation to fail with 419.

Should I call /sanctum/csrf-cookie before login?

Yes. For Sanctum’s cookie-based SPA authentication, the frontend should initialize CSRF protection before submitting the login request.

Why does login work but /api/user return 401?

The login may have succeeded while subsequent requests are not sending the session cookie, or Sanctum may not recognize the frontend as stateful. Check SANCTUM_STATEFUL_DOMAINS, session cookies, credentials, middleware, and the browser Network panel.

Can Laravel Sanctum authenticate a React or Vue SPA?

Yes. Sanctum is designed to support first-party SPAs, including React and Vue applications, using Laravel’s session-cookie authentication when the frontend and backend architecture meets Sanctum’s requirements.

Can my SPA be on Vercel while Laravel is on another domain?

A frontend such as project.vercel.app and an API at api.example.com do not share the same top-level domain. For Sanctum's first-party session authentication, using a frontend custom domain such as app.example.com is generally a cleaner architecture.

The Most Important Debugging Rule

Do not change five configuration files at once.

Find the first failing request.

Then fix that layer.

If OPTIONS fails, debug CORS.

If the CSRF endpoint fails, debug the route and browser communication.

If the CSRF endpoint succeeds but login returns 419, debug the XSRF token and session cookies.

If login succeeds but authenticated API calls return 401, debug the session and Sanctum’s stateful request recognition.

That approach is much faster than copying random .env configurations from old Stack Overflow answers.

I maintain a more detailed production guide covering domain layouts, Laravel 13 configuration, localhost edge cases, Axios, cookies, CORS, 401 vs 419 vs 422 responses, and deployment troubleshooting here:

👉 Complete Laravel Sanctum CORS 419 Error Fix for Laravel 13

Use that checklist when a basic configuration looks correct but authentication still fails in production.

Laravel Sanctum authentication is reliable once every layer agrees on one thing:

which frontend is trusted, which cookie belongs to the session, and how that credential reaches Laravel.

Debug those relationships instead of treating 419 as a single CORS error, and most Sanctum authentication problems become much easier to isolate.

--

--

Written by Ankit Khoiwal

beyond the code, a story, I publish in-depth developer guides & roadmaps at https://www.blogs-world.in/