Demystifying Host Header Injection
Introduction
In modern web architecture, multiple websites or applications are frequently hosted on a single server or routed through a single reverse proxy (like Nginx or HAProxy). To determine which application a user is trying to reach, the server relies on the HTTP Host header.
Host Header Injection occurs when a web application inherently trusts the user-supplied Host header and uses it in unsafe ways—such as generating links, determining routing, or evaluating business logic—without proper validation or sanitization.
The Anatomy of a Request
A standard HTTP/1.1 request looks like this:
1
2
3
GET /profile HTTP/1.1
Host: [www.vulnerable-app.com](https://www.vulnerable-app.com)
Authorization: Bearer <token>
The application might use the Host header dynamically in its backend code (e.g., PHP, Node.js, Python):
$reset_link = "https://" . $_SERVER['HTTP_HOST'] . "/reset?token=" . $token;
If an attacker modifies the Host header to evil-attacker.com, the application might blindly process it, leading to several critical attack vectors.
Attack Techniques & Vectors
1. Password Reset Poisoning
This is one of the most common and impactful consequences of Host Header Injection. If an application uses the Host header to dynamically generate the URL sent in a password reset email, an attacker can hijack another user’s account.
Technical Execution:
The attacker initiates a password reset for the victim’s account but intercepts the request to modify the Host header.
1
2
3
4
5
POST /api/v1/password-reset HTTP/1.1
Host: attacker.com
Content-Type: application/json
{"email": "[email protected]"}
ASCII Flowchart:
[ Attacker ]
|
| 1. POST /reset
| Host: attacker.com
| email: [email protected]
V
[ Vulnerable Application ]
|
| 2. Generates token.
| Builds link: [https://attacker.com/reset?token=XYZ](https://attacker.com/reset?token=XYZ)
| 3. Sends email to Victim.
V
[ Victim's Inbox ]
|
| 4. Victim opens email and clicks "Reset Password"
V
[ Attacker's Server ]
|
+--> 5. Captures: GET /reset?token=XYZ
(Attacker now has the victim's valid reset token!)
2. Web Cache Poisoning
When an application sits behind a caching server (like Varnish, Cloudflare, or Fastly), the cache saves responses based on a “cache key” (usually the URL path and query parameters). If the application uses the Host header to generate absolute URLs (e.g., for importing JavaScript files) but the caching server does not include the Host header in the cache key, the cache can be poisoned.
Technical Execution:
The attacker sends a crafted request asking for a static page, injecting their domain.
1
2
GET /login HTTP/1.1
Host: attacker.com
The backend responds with poisoned HTML:
1
2
3
4
<html>
<head>
<script src="[https://attacker.com/assets/main.js](https://attacker.com/assets/main.js)"></script>
</head>
ASCII Flowchart:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[ Attacker ]
|
| 1. GET /login
| Host: attacker.com
V
[ Cache Server ] -- 2. Cache Miss: Forwards to Backend --> [ Backend App ]
| |
| <--- 3. Returns HTML with attacker.com scripts ------------+
|
| 4. Caches response mapped to the "/login" path
|
[ Victim ]
|
| 5. Normal GET /login
| Host: target.com
V
[ Cache Server ]
|
+--- 6. Serves the poisoned cached page ---> [ Victim Executed Malicious JS ]
3. Accessing Internal Systems & SSRF (Routing By-pass)
Sometimes, reverse proxies or load balancers route requests to internal administrative panels if they recognize a specific internal hostname. By manipulating the Host header, an external attacker might bypass access controls.
Technical Execution:
An attacker discovers that an internal admin panel runs on the same server but is blocked from external access.
1
2
GET /admin HTTP/1.1
Host: localhost
OR
1
2
GET /admin HTTP/1.1
Host: internal-admin.local
If the front-end proxy routes the request based solely on the Host header without verifying the origin of the connection, it may forward the attacker to the protected internal application, effectively acting as a Server-Side Request Forgery (SSRF) or routing bypass.
ASCII Flowchart:
[ External Attacker ]
|
| 1. GET /admin
| Host: internal-admin.local
V
[ Public Reverse Proxy / WAF ]
|
| 2. Trusts the Host header blindly.
| Routes connection internally based on "internal-admin.local"
V
[ Protected Internal Admin Panel ]
|
| 3. Processes request, returns sensitive dashboard HTML
V
[ External Attacker ] <--- (Bypass Successful - Attacker sees internal panel)
4. Overriding via Proxy Headers
If the primary Host header cannot be manipulated (e.g., due to strict WAF rules or TLS SNI checks), attackers can often achieve the same injection by utilizing secondary headers that are trusted by intermediate proxies or load balancers.
Common override headers include:
X-Forwarded-HostX-HostX-Forwarded-Server
Technical Execution:
1
2
3
GET / HTTP/1.1
Host: [www.target.com](https://www.target.com)
X-Forwarded-Host: attacker.com
If the backend framework prioritizes the X-Forwarded-Host over the standard Host header, the injection is successful.
ASCII Flowchart:
[ Attacker ]
|
| 1. GET /
| Host: [www.target.com](https://www.target.com)
| X-Forwarded-Host: attacker.com
V
[ Intermediate Proxy / WAF ]
|
| 2. Validates standard Host: [www.target.com](https://www.target.com) (Rules passed)
| Passes custom headers downstream unchanged
V
[ Backend Application ]
|
| 3. Framework logic prioritizes X-Forwarded-Host over Host
| $host = $_SERVER['HTTP_X_FORWARDED_HOST'];
| 4. App uses "attacker.com" to build URLs/Logic
V
( Injection Successful )
Remediation Strategies
To effectively defend against Host Header Injection, defense-in-depth is required:
- Absolute URLs: Avoid using the
Hostheader to generate absolute URLs. Use relative URLs where possible. - Configuration Whitelisting: If absolute URLs are necessary (e.g., for emails), rely on a strictly configured, server-side environment variable (like a
BASE_URLconstant) rather than the dynamic header. - Strict Routing: Configure web servers (Nginx, Apache) to reject or drop requests where the
Hostheader does not match a strict whitelist of permitted domains. - Ignore Override Headers: Do not blindly trust
X-Forwarded-Hostor similar headers unless the application is explicitly expecting them from a trusted, known downstream proxy.