HTTP Response Splitting in NGINX

Learn how CRLF injection attacks exploit NGINX configurations and how to prevent header injection vulnerabilities.

Updated: January 2025 7 min read

What is HTTP Response Splitting?

HTTP Response Splitting (also known as CRLF Injection) is a vulnerability where an attacker can inject newline characters (\r\n or \n) into HTTP response headers. This can lead to:

  • Cache poisoning attacks
  • Cross-site scripting (XSS)
  • Session fixation
  • Response manipulation

Why This is Critical

HTTP Response Splitting can allow attackers to:

  • Inject malicious headers into responses
  • Create fake responses that get cached by proxies
  • Inject arbitrary HTML/JavaScript into responses
  • Bypass security controls and redirect users

How It Works in NGINX

The attack occurs when user-controlled input is used in directives that set HTTP headers or URLs. If the input contains newline characters, the attacker can terminate the current header and inject additional headers or even a complete response body.

Attacker sends: GET /page?url=http://evil%0d%0aX-Injected:header
NGINX generates: Location: http://evil\r\nX-Injected:header
%0d%0a is the URL-encoded CRLF sequence

Vulnerable NGINX Directives

These NGINX directives are susceptible to HTTP splitting when they contain user-controlled variables:

  • rewrite — URL rewriting with redirects
  • return — Returning redirects or custom responses
  • add_header — Adding custom response headers
  • proxy_set_header — Setting headers for upstream requests
  • proxy_pass — Proxying to dynamic backends

Dangerous Variables

These NGINX variables can contain newline characters when controlled by attackers:

  • $uri — The normalized request URI (can be manipulated via rewrites)
  • $document_uri — Same as $uri
  • $request_uri — The original request URI including query string
  • $arg_* — Query string parameters
  • $http_* — Any HTTP request header
  • Regex capture groups ($1, $2, etc.) without proper anchoring

Vulnerable Patterns

Pattern 1: Redirect with User Input

# User can inject headers via $uri
rewrite ^ http://$host$uri permanent;

# Attack: GET /page%0d%0aSet-Cookie:evil=value HTTP/1.1

Pattern 2: Return with Query Parameter

# Attacker controls the redirect URL
location /redirect {
    return 301 $arg_url;
}

# Attack: /redirect?url=http://evil%0d%0aX-Injected:true

Pattern 3: Custom Header from User Input

# User-controlled header value
add_header X-Original-URI $uri;

# If $uri is manipulated, attacker can inject headers

Pattern 4: Proxy Header with User Data

# User controls the forwarded URI
proxy_set_header X-Original-Uri $document_uri;
proxy_pass http://backend;

# Injection point for header manipulation

Pattern 5: Regex Capture Groups

# Unanchored regex allows injection
location ~ /proxy/(.*) {
    set $path $1;
    proxy_pass http://backend/$path;
}

# Attack: GET /proxy/page%0d%0aX-Injected:value HTTP/1.1

Secure Patterns

Use Hardcoded Values

# Redirect to a fixed location
return 301 https://example.com/;

# No user input in the redirect target

Validate and Sanitize

# Use map to whitelist allowed redirects
map $arg_page $redirect_url {
    default         "";
    "home"          "/";
    "about"         "/about";
    "contact"       "/contact";
}

location /redirect {
    if ($redirect_url = "") {
        return 400;
    }
    return 301 $redirect_url;
}

Use Safe Variables

# $host is safer than $http_host for redirects
# But still validate the destination domain
return 301 https://$host$request_uri;

Anchor Regex Patterns

# Properly anchored regex limits what can be captured
location ~ ^/api/v[12]/([a-zA-Z0-9_]+)$ {
    # $1 can only contain alphanumeric and underscore
    proxy_pass http://backend/$1;
}

Detecting with Gixy

Gixy automatically detects HTTP Response Splitting vulnerabilities in your NGINX configuration:

$ gixy /etc/nginx/nginx.conf

==================== Results ====================

[http_splitting] Possible HTTP-Splitting vulnerability.
  Severity: HIGH
  Description: Using variables that can contain "\n" or "\r"
               may lead to http injection.
  Reason: At least variable "$uri" can contain "\n"
  Pseudo config:
      server {
          location / {
              rewrite ^ http://$host$uri;
          }
      }
  File: /etc/nginx/conf.d/site.conf
  Line: 12

==================== Summary ====================
Total issues: 1 (High: 1)
Best Practice: Run gixy as part of your deployment process to catch HTTP splitting vulnerabilities before they reach production.

Real-World Impact

HTTP Response Splitting has been used in real attacks to:

  • Deface websites — By injecting HTML content into cached responses
  • Steal sessions — By injecting Set-Cookie headers
  • Phish users — By redirecting to malicious sites via cache poisoning
  • Bypass WAFs — By splitting requests to evade detection

Prevention Checklist

  • Never use $uri, $document_uri, or $request_uri in redirects
  • Never use $arg_* variables directly in headers or URLs
  • Never use $http_* variables without validation
  • Always anchor regex patterns with ^ and $
  • Restrict capture groups to safe character classes like [a-zA-Z0-9_-]
  • Use map directives to whitelist allowed values
  • Run Gixy in your CI/CD pipeline to catch issues automatically

Further Reading