Checking what a hostname resolves to and then handing the hostname to an HTTP client is not safe. The client may look it up again and get a different answer. OWASP's fix is to validate the resolved IPs, then connect only to those validated addresses, keeping the original hostname for the Host header, SNI and certificate checks.
The race in plain terms
Suppose your server accepts a URL and wants to block internal targets.
- It resolves
files.example.netand sees a public IP. Check passes. - It passes the URL to an HTTP library.
- The library resolves the name again. This time the attacker's DNS server returns an internal address.
That second lookup is a time of check to time of use gap. OWASP puts it flatly: checking DNS answers separately does not prevent DNS rebinding.
The recommended approach
From the OWASP SSRF cheat sheet:
- Resolve the name and validate each returned IP against permitted networks.
- Connect only to a validated address.
- Keep the original hostname for the Host header, TLS server name and certificate validation, so HTTPS still works.
- Apply the same policy to retries and fallback connections, since those can resolve again.
Many HTTP libraries let you plug in a custom resolver or connection hook. Put the check there, in the same code path that opens the socket.
Other ways validation goes wrong
Redirects. A permitted URL can redirect to a forbidden one. Disable automatic following, or revalidate each hop with the same connection time check.
Parser differences. Two parsers can read different hosts from the same string. OWASP's example: http://example.com\@evil.com. A WHATWG style parser reports example.com as the host, while CPython's urllib.parse reports evil.com. The cheat sheet's rule is to reject a URL whose host is not read identically by every parser in play, rather than trying to reconcile them.
Monitoring is not enforcement. Alerting when an allowlisted domain starts resolving to a non public address is useful for detection. OWASP says it is not a substitute for enforcement at connection time.
A test you can run
- Point a test domain you control at a public address, then switch it to a private address with a very short TTL.
- Submit it through your URL fetching feature.
- Confirm the connection to the private address is refused, even on a second attempt.
- Repeat with a redirect from a public host to a private one.
- Repeat with an odd URL, such as one containing a backslash before an at sign, and confirm it is rejected.
Defence in depth
Application checks can fail. Add network level limits: give the fetching service only the routes it needs and keep it away from metadata services and internal admin endpoints. For AWS, OWASP recommends moving to IMDSv2 and turning off IMDSv1.
Quick check
If I resolve the hostname once and keep the IP, do I still need the hostname?
Yes, for HTTPS. You still need the original name for the Host header, SNI and certificate checks.
Sources
Last reviewed: 19 October 2026.