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

HTTP Response Splitting

Security CWE-113 OWASP A3:2021 CVSS 6.1 PHP 5.0+ Advanced
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5), because detection_hints.tools lists semgrep and owasp-zap — both specialist SAST/DAST tools. The vulnerability is not caught by a compiler or default linter; it requires either a dedicated static analysis rule (semgrep) or dynamic scanning (owasp-zap) to surface reliably.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3), because the quick_fix describes stripping \r and \n from user input before passing to header() calls — a small, targeted change. For PHP 7.4+ it is essentially a version-upgrade fix; for older PHP it is a consistent input-sanitisation pattern applied wherever header() accepts user-controlled values, which may touch a handful of call sites but stays within one component.

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

Closest to 'localised tax' (b3), because applies_to scopes this to web and api contexts only, not CLI or queue workers. The fix burden falls on places that reflect user input into HTTP headers — a relatively localised concern rather than something that shapes the whole architecture.

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

Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7), because the misconception explicitly states developers believe modern frameworks are immune, and common_mistakes include trusting that PHP's header() sanitises input (it does not in all versions) and missing that URL-decoded values reintroduce the hazard. The 'obvious' assumption — that the framework or built-in function handles this — is wrong in many real scenarios, making it a serious cognitive trap.

About DEBT scoring →

Also Known As

response splitting header splitting CRLF response split

TL;DR

Injecting CRLF sequences into HTTP headers causes the server to emit two separate responses, enabling cache poisoning and XSS.

Explanation

HTTP Response Splitting is an escalation of CRLF injection. By injecting \r\n\r\n into a header value, an attacker can terminate the first response and craft an entirely fabricated second response — including its own headers and body. Shared proxies and caches may store the attacker-injected second response and serve it to other users. Modern PHP (7.4+) strips newlines from header() calls, but older code or custom header-writing functions remain vulnerable. Always sanitise header values and use framework-level response objects rather than raw header() calls.

How It's Exploited

An attacker submits a redirect URL containing \r\n\r\n followed by a crafted HTML body. A proxy caches the fabricated response and serves the malicious content to every subsequent visitor who requests the same URL.

Common Misconception

Modern frameworks are immune to response splitting. Most are, but custom header() calls or third-party libraries accepting unvalidated user input can still be vulnerable.

Why It Matters

Injecting newline characters into HTTP response headers allows an attacker to craft a second HTTP response, potentially poisoning caches or injecting malicious content for other users.

Common Mistakes

  • Reflecting user input into any HTTP header without stripping \r and \n characters.
  • URL-decoding values before passing to header() without re-stripping control characters.
  • Not realising this is the underlying mechanism behind CRLF injection and header injection.
  • Trusting that PHP's header() function sanitises its input — it does not in all versions.

Code Examples

✗ Vulnerable
// User input injected into header — response splitting:
$lang = $_GET['lang']; // Attacker: en\r\nHTTP/1.1 200 OK\r\n...
header('Content-Language: ' . $lang);
// Results in a second fabricated HTTP response

// Safe:
$lang = preg_replace('/[\r\n]/', '', $_GET['lang']);
header('Content-Language: ' . $lang);
✓ Fixed
// Validate and encode header values before sending:
function safeRedirect(string $url): void {
    // Validate URL — only allow safe schemes and known domains:
    $parsed = parse_url($url);
    if (!in_array($parsed['scheme'] ?? '', ['http', 'https'], true)) {
        throw new InvalidArgumentException('Invalid URL scheme');
    }

    // PHP 7.4+ header() strips CR/LF — but be explicit:
    $safeUrl = preg_replace('/[\r\n]/', '', $url);
    header('Location: ' . $safeUrl, true, 302);
    exit;
}

// For custom headers:
$safeValue = preg_replace('/[\r\n\0]/', '', $userInput);
header('X-Custom: ' . $safeValue);

Added 15 Mar 2026
Edited 22 Mar 2026
Views 44
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 1 ping T 1 ping W 0 pings T 1 ping 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
No pings yet today
No pings yesterday
Amazonbot 7 Scrapy 5 ChatGPT 3 Ahrefs 3 SEMrush 3 Google 2 Claude 2 Majestic 1 Perplexity 1 Bing 1 Meta AI 1 Sogou 1
crawler 28 crawler_json 2
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
PHP 7.4+ automatically strips CR/LF from header values — for older PHP, strip \r and \n from any user input before using it in header() calls
📦 Applies To
PHP 5.0+ web api
🔗 Prerequisites
🔍 Detection Hints
header() with user-controlled value containing potential CRLF; Location header built from user input without CR/LF stripping; PHP < 7.4 not stripping newlines
Auto-detectable: ✓ Yes semgrep owasp-zap
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line
CWE-113 CWE-93


✓ schema.org compliant