← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

CSRF Token Handling in Fetch & Axios

javascript ES2017 Intermediate
debt(d7/e3/b3/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

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.

e3 Effort Remediation debt — work required to fix once spotted

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.

b3 Burden Structural debt — long-term weight of choosing wrong

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.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

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.

About DEBT scoring →

Also Known As

CSRF token JS X-CSRF-Token XSRF-TOKEN Laravel CSRF JavaScript

TL;DR

Including PHP-generated CSRF tokens in JavaScript requests — reading from meta tags or cookies and attaching to every state-changing request.

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

SameSite=Lax cookies make CSRF tokens unnecessary for all JavaScript requests — SameSite=Lax only blocks cross-site form submissions and some navigation; programmatic fetch() from the same origin still needs CSRF tokens if using cookie auth.

Why It Matters

Every state-changing AJAX request to PHP must include the CSRF token — omitting it causes Laravel/Symfony to return 419 Token Mismatch or 403 errors.

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

✗ Vulnerable
// No CSRF token — Laravel returns 419:
fetch('/api/order', { method: 'POST', body: JSON.stringify(order) });
✓ Fixed
// 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';

Added 17 Mar 2026
Edited 22 Mar 2026
Views 186
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 2 pings T 1 ping W 2 pings T 3 pings F 6 pings S 3 pings S 8 pings M 1 ping T 0 pings W
No pings yet today
ChatGPT 1
ChatGPT 86 Perplexity 36 Amazonbot 15 Google 9 Unknown AI 6 Ahrefs 3 Majestic 2 SEMrush 2 DuckDuckGo 1 Meta AI 1
crawler 149 crawler_json 7 pre-tracking 5
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Read the CSRF token from <meta name='csrf-token'> and include as X-CSRF-Token header on all POST/PUT/PATCH/DELETE fetch requests
📦 Applies To
javascript ES2017 web laravel symfony
🔗 Prerequisites
🔍 Detection Hints
fetch() POST without X-CSRF-Token or X-XSRF-TOKEN header; Laravel returns 419 on AJAX POST
Auto-detectable: ✓ Yes semgrep eslint
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✗ Manual fix Fix: Medium Context: File Tests: Update
CWE-352

✓ schema.org compliant