Опція credentials визначає, чи надсилає fetch cookies і чи приймає їх з відповіді:
'same-origin'(за замовчуванням) - лише для запитів на той самий домен;'include'- і для інших доменів (потрібна відповідна CORS-конфігурація сервера);'omit'- ніколи.
Для звичайного Laravel-застосунку, де сторінки й запити з одного домену, сесійна cookie надсилається автоматично.
CSRF-захист у Laravel перевіряє, що запит, який змінює дані (POST, PUT, PATCH, DELETE), справді надіслано з вашого сайту. Способи передати токен з JavaScript:
1. Заголовок X-CSRF-TOKEN з мета-тегу:
<meta name="csrf-token" content="{{ csrf_token() }}">
await fetch('/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
},
body: JSON.stringify(order),
});
2. Заголовок X-XSRF-TOKEN з cookie. Laravel ставить cookie XSRF-TOKEN (зашифрований токен, доступний JavaScript). Axios автоматично читає її і додає заголовок для запитів на той самий домен - тому в Laravel-проєктах з axios про CSRF зазвичай не думають. З fetch це треба робити самому.
3. Поле _token у тілі запиту - якщо відправляється FormData з форми, де є @csrf.
Laravel 13 перевіряє ще й джерело запиту. Middleware PreventRequestForgery пропускає запит без токена, якщо браузер надіслав заголовок Sec-Fetch-Site: same-origin. Сучасні браузери додають його самі, а підробити з іншого сайту неможливо. Токен лишається запасним механізмом для старих браузерів і запитів без цього заголовка.
Помилка 419 (Page Expired / CSRF token mismatch) - токен не передано, він застарів (сесія закінчилася, поки сторінка була відкрита) або сесійна cookie не дійшла. Для довго відкритих сторінок варто обробляти 419: оновити токен чи запропонувати перезавантажити сторінку.
SPA на іншому піддомені зі Sanctum:
await fetch('https://api.example.com/sanctum/csrf-cookie', { credentials: 'include' });
await fetch('https://api.example.com/api/orders', {
method: 'POST',
credentials: 'include',
headers: { 'X-XSRF-TOKEN': decodeURIComponent(getCookie('XSRF-TOKEN')), Accept: 'application/json' },
body: JSON.stringify(order),
});
Потрібні також supports_credentials у config/cors.php, домен SPA у SANCTUM_STATEFUL_DOMAINS і спільний домен сесійних cookies.
Без Accept: application/json Laravel на помилку валідації відповість редиректом, а не JSON з помилками.