Laravel 13 2,157 formalized its CSRF middleware as PreventRequestForgery, with two layers; VerifyCsrfToken and ValidateCsrfToken survive as deprecated subclasses. GET, HEAD and OPTIONS always pass. A state-changing request passes if the browser's Sec-Fetch-Site header says same-origin. A page cannot forge that header (browsers set it, and scripts may not), and it has been Baseline since March
Otherwise the middleware falls back to the classic token of CSRF Tokens: _token in the body
(which Blade's @csrf writes as a hidden input), an X-CSRF-TOKEN header, or X-XSRF-TOKEN holding the encrypted XSRF-TOKEN cookie that Axios 109,234 echoes, compared with the session's token through hash_equals. Failure answers 419.
B=http://127.0.0.1:8304; C='curl -s -o /dev/null -w %{http_code}\n'
T=$(curl -s -c jar $B/cart | grep -o 'name="_token" value="[^"]*' | cut -d'"' -f4)
$C -d isbn=1 $B/cart # no token, no header
$C -b jar -d "_token=$T&isbn=2" $B/cart # session cookie + token
$C -H 'Sec-Fetch-Site: same-origin' -d isbn=3 $B/cart
$C -H 'Sec-Fetch-Site: cross-site' -d isbn=4 $B/cart
$C -d '{}' $B/webhooks/payment419 200 200 419 204
The token request needed the session cookie from the jar, while the header check needed no session at all. except: ['webhooks/*'] from Registering Middleware lets a payment provider post without either; verify such calls with the provider's signature instead. preventRequestForgery(originOnly: true) drops the token fallback: with it, the no-token and cross-site requests returned 403 instead of 419 and no XSRF-TOKEN cookie was set. Browsers send Sec-Fetch-Site only to secure origins (HTTPS or localhost), so keep the fallback unless every user arrives over HTTPS; allowSameSite: true also admits sibling subdomains. curl 3,008 can send any header, which is harmless because CSRF abuses a victim's browser and its cookies. Feature tests skip the check (Feature Tests), and SPAs use Sanctum 2,988 's flow (Sanctum for SPAs).