Because the Host header is supplied by the requester. If your app builds the reset link in the email from it, an attacker can request a reset for a victim with a forged Host value, and the victim receives a genuine email whose link points to the attacker's server. Use a fixed base URL.
Definition
Host header injection in password reset, sometimes called reset poisoning, is a flaw where the application trusts the Host header, or a similar forwarded header, to compose absolute URLs. The OWASP Forgot Password Cheat Sheet says not to rely on the Host header when creating reset URLs, to avoid Host Header Injection, and to hard code the base URL or validate it against a list of trusted domains.
How the attack plays out
- The attacker submits a reset request for the victim's address and changes the Host header to a domain they control.
- Your server creates a valid token and emails the victim a link built from the forged host.
- The victim opens the email, which really came from you, and clicks.
- The link leads to the attacker's server, which captures the token, then uses it on your real site.
Many users click because the email passes every authentication check. That is why a fix in email authentication does not help here. The bad part is inside your own message.
Related header trouble
Reverse proxies add forwarded host headers. If your framework trusts them from any client, the same problem returns through a different header. Trust forwarded values only from your own proxy. See trusting proxy headers safely.
Other reset link rules from OWASP
| Rule | Why |
|---|---|
| Consistent response for known and unknown accounts | Avoids confirming which accounts exist |
| Cryptographically secure tokens, long enough to resist brute force | Prevents guessing |
| Single use and expiry | Limits the value of a stolen link |
| HTTPS links | Protects the token in transit |
| No-referrer policy on the reset page | Stops the token leaking to third party resources |
| Rate limit requests | Prevents flooding a user's inbox |
Steps
- Search your code for any place a URL is built from the request host, in emails especially.
- Replace it with a configuration value for the public base URL per environment.
- If you host many tenants, validate the host against an allow list before using it.
- Configure your framework's allowed hosts setting. See Django ALLOWED_HOSTS explained.
- Add a test that sends a forged Host header and asserts the emailed link still uses your domain.
- Review other email links, such as invites and confirmations, with the same test.
Where Safeguard fits
Safeguard's web application scanning (DAST) and code scanning help find places where request headers flow into generated links, and its dependency scanning flags framework versions with known issues. The free scan is at https://gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen.
Quick check
Does SPF, DKIM and DMARC stop this attack?
No. The email is genuinely sent by you. The harmful link is built by your own code.
What replaces the Host header?
A configured base URL, or a host checked against a list of trusted domains.
Sources
Last reviewed: 19 October 2026.