# Laravel and Vue CORS errors

> A same-origin Inertia app does not need CORS. A separate SPA does, and a 419 is a different failure from a blocked response.

- URL: https://laravel-vuejs.space/laravel-vue-cors
- Author: Anass Ez-zouaine (https://laravel-vuejs.space/author/anass-ez-zouaine)
- Published: 2026-10-10
- Topics: Laravel, VueJs, Sanctum

The console says “blocked by CORS policy”, you add `'*'` everywhere, and login still fails. That’s because half of these errors are not CORS at all. Here is how to tell which bug you have, and the exact config for the one that is.

> **TL;DR**
>
> -   Same-origin Inertia apps never need CORS.
> -   A separate SPA: list its exact origin, add `login`/`logout` to `paths`, set `supports_credentials` to `true`.
> -   Never pair `allowed_origins: ['*']` with credentials.
> -   Failed preflight = CORS. 419 = CSRF/session. 401 = no session. Only the first is fixed in `cors.php`.
>
> **What you'll learn**
>
> -   When a Laravel + Vue app needs CORS at all
> -   The `config/cors.php` values for a cookie-based SPA
> -   How to tell CORS from a 419 or a 401
>
> **Who it's for:** Developers running Vue on a different origin from Laravel
>
> **Applies to:** Laravel 13 · Sanctum · Vue 3 · Axios

Most “Laravel Vue CORS” threads are two different bugs with the same console paragraph. One is a cross-origin SPA. The other is a CSRF failure that only looks like CORS because the browser hides the status. This page separates them.

> **Heads up: Inertia does not need this**
>
> The Inertia starter kit is same-origin. You open the Laravel URL, and `@vite` loads the dev assets. The browser never makes a cross-origin call, so `config/cors.php` is not the fix. If that app is what you are building, the cookie and login steps are in [Sanctum for a Vue SPA](https://laravel-vuejs.space/laravel-sanctum-vue-spa) only when the front end is a second origin. The architecture choice is [Inertia, a SPA, or Nuxt](https://laravel-vuejs.space/laravel-vue-in-2026).

## When the browser sends a preflight

A separate Vue app on `http://localhost:5173` calling `http://localhost:8000` is a different origin. The port counts. A POST with `Content-Type: application/json`, or with `X-XSRF-TOKEN`, triggers an OPTIONS preflight before the real request. If that OPTIONS response has no `Access-Control-Allow-Origin` for the Vue origin, the browser reports a CORS error and your Vue `catch` never sees the body.

> **Gotcha: '\*' with credentials**
>
> Publish the config and turn credentials on. Sanctum’s cookie flow needs credentials. List the SPA origin. Do not leave `allowed_origins` as `['*']` once credentials are on. The browser rejects a literal `Access-Control-Allow-Origin: *` on a credentialed response, but Laravel’s CORS library does not send that star in this case. It reflects the request origin and then allows every caller.

```bash
php artisan config:publish cors
```

`config/cors.php`
```diff
- 'paths' => ['api/*', 'sanctum/csrf-cookie'], 
+ 'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout'], 
  'allowed_methods' => ['*'],
  'allowed_origins' => ['http://localhost:5173'],
  'allowed_headers' => ['*'],
  'supports_credentials' => true,
```

The default path list covers `api/*` and `sanctum/csrf-cookie`. It does not cover `POST /login`. Login lives on the web middleware group, which is the point of the Sanctum guide. If the preflight for `/login` never matches a path, the login request fails before Laravel runs the controller. Add each web path the SPA actually calls. Do not copy this list and then invent extra routes.

> **Tip: copy the origin from the browser**
>
> `allowed_origins` is the Vue origin, including the scheme and the port. `http://localhost:5173` and `http://127.0.0.1:5173` are not the same origin. Use the one the browser shows.

## The cookie settings that sit next to CORS

CORS only allows the browser to read the response. The session cookie still has to be sent. `SANCTUM_STATEFUL_DOMAINS` needs the SPA host and port, such as `localhost:5173`, and the Laravel host you open. `supports_credentials` on the CORS config is what lets the browser store and send that cookie.

> **Gotcha: a leading dot on localhost**
>
> Leave `SESSION_DOMAIN` empty on localhost. Set it to `.example.com` only when the SPA and the API are real subdomains. A leading dot on `localhost` drops the cookie. Axios still needs `withCredentials` and `withXSRFToken`. Those lines are in the Sanctum guide. This page does not repeat the login form.

## 419 is not CORS

Open the failing request in the network panel.

| What you see | What it is | Where to fix it |
| --- | --- | --- |
| `(failed)`, or “preflight blocked” in the console | CORS | Origin, `paths`, `supports_credentials` |
| **419** | CSRF cookie or session domain | [Sanctum guide](https://laravel-vuejs.space/laravel-sanctum-vue-spa) |
| **401** from `auth:sanctum` | No session | Log in again |

If the status is `(failed)` or the console says the preflight was blocked, it is CORS. Check the origin, the path list, and `supports_credentials`.

If the status is **419**, Laravel answered. The CSRF cookie was missing or the session domain was wrong. Fix that in the Sanctum guide. Changing `allowed_origins` will not turn a 419 into a 204.

A **401** from `auth:sanctum` means the request arrived and there is no session. That is also not CORS. Send the user back through `GET /sanctum/csrf-cookie` and `POST /login`.

## Where this stops

Nuxt on port 3000 is the same rule with a different origin. The call itself is [Nuxt in front of the API](https://laravel-vuejs.space/nuxt-laravel-api). The JSON shape is [API resources](https://laravel-vuejs.space/laravel-api-resources-vue).

## Recap

- Same-origin Inertia: no CORS config needed.
- Separate SPA: exact origin, every web path it calls, credentials on.
- `SANCTUM_STATEFUL_DOMAINS` and `SESSION_DOMAIN` sit next to CORS; set both.
- Read the status code before touching `cors.php`.

## FAQ

**Q: Does a Laravel Inertia Vue app need CORS?**

No. The page and the requests share one origin, so the browser never makes a cross-origin call.

**Q: Why does my Vue SPA login fail with a CORS error?**

The default `paths` list does not include `login`. Add `login` and `logout`, list the SPA origin, and set `supports_credentials` to `true`.

**Q: Is a 419 a CORS error?**

No. Laravel answered with a CSRF failure. Fetch `/sanctum/csrf-cookie` first and check `SESSION_DOMAIN`.

**Q: Are localhost and 127.0.0.1 the same origin?**

No. Use the exact scheme, host and port the browser shows for the Vue app.

## Next steps

- [Sanctum for a Vue SPA](https://laravel-vuejs.space/laravel-sanctum-vue-spa): the login flow on the other side of this config.
- [Nuxt in front of a Laravel API](https://laravel-vuejs.space/nuxt-laravel-api)
- [Laravel Fortify with Vue](https://laravel-vuejs.space/laravel-fortify-vue)

## Sources

- [Laravel 13: CORS](https://laravel.com/docs/13.x/routing#cors)
- [Laravel 13: Sanctum, SPA authentication](https://laravel.com/docs/13.x/sanctum#spa-authentication)
- [MDN: CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS)
