CSRF Token Handling in Fetch & Axios
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list semgrep and eslint as tools, but a missing CSRF header on a fetch() call typically surfaces only at runtime as a 419/403 error — not as a compile-time or default-linter warning. Semgrep rules for this pattern exist but require custom or specialist configuration, nudging above d5 toward d7. The symptom (HTTP error in production or manual testing) is the most common discovery path.
Closest to 'simple parameterised fix' (e3). The quick_fix states reading the token from <meta name='csrf-token'> and adding it as a header on all relevant requests. This is more than a single-line patch (e1) because it requires updating every fetch/Axios call site or centralising in an interceptor/wrapper, but it stays within one component or a small refactor pattern rather than spanning multiple files architecturally.
Closest to 'localised tax' (b3). The applies_to scope is web contexts only, and the burden is a recurring but contained discipline — ensuring the token header is present on state-changing requests. It doesn't reshape the whole architecture, but it does impose a persistent check on every new AJAX call added to the codebase, keeping it at b3 rather than b1.
Closest to 'notable trap' (t5). The misconception field explicitly states that developers believe SameSite=Lax cookies make CSRF tokens unnecessary for programmatic fetch() requests — a documented gotcha that many developers hit after modern browser SameSite defaults changed. It's a well-known but non-obvious trap, fitting t5 rather than t7 because it is broadly documented and doesn't contradict an analogous concept from another ecosystem.
Also Known As
TL;DR
Explanation
PHP generates a CSRF token per session (Laravel, Symfony). JavaScript must include it on POST/PUT/PATCH/DELETE. Two patterns: meta tag (Laravel: <meta name='csrf-token' content='{{ csrf_token() }}'>) read via querySelector; cookie (XSRF-TOKEN) read via document.cookie or axios which reads it automatically (same-origin). SPA with separate PHP API: include token in initial page data (window.__csrf), API responses (custom header), or refresh endpoint.
Common Misconception
Why It Matters
Common Mistakes
- Not refreshing the CSRF token after session expiry
- Missing CSRF token on multipart fetch (FormData) requests
- Hardcoding the token instead of reading it dynamically from meta tag
Code Examples
// No CSRF token — Laravel returns 419:
fetch('/api/order', { method: 'POST', body: JSON.stringify(order) });
// Read from meta tag (Laravel/Symfony pattern):
const csrf = document.querySelector('meta[name=csrf-token]')?.content;
// Attach to all state-changing requests:
async function post(url, data) {
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf,
},
body: JSON.stringify(data),
});
}
// Axios reads XSRF-TOKEN cookie automatically for same-origin:
// axios.defaults.xsrfCookieName = 'XSRF-TOKEN';
// axios.defaults.xsrfHeaderName = 'X-XSRF-TOKEN';