A service accepts a URL from the client and fetches it to generate a link preview. A teammate proposes blocking requests to URLs containing `169.254.169.254` or `localhost`. Why is that an incomplete defense against SSRF, and what would a defense-in-depth version actually look like?
A block-list of known-bad strings loses to representation tricks the block-list author didn't think of: the same address can be written as a decimal integer, in IPv6-mapped form, or resolved through a DNS name an attacker controls that points at an internal address only at request time (DNS rebinding) — none of which contain the literal string 169.254.169.254. The service is inside the network and, if it runs with any cloud role or reaches internal-only services, an attacker who gets it to fetch an internal URL gets back whatever that internal endpoint returns, including cloud credentials from a metadata endpoint. A real defense stacks several layers: allow-list which hosts may be fetched (not block-list); resolve the hostname to an actual IP address yourself and reject private, loopback and link-local ranges before connecting; don't follow redirects blindly, or re-check the address after every hop; and route the fetch through an egress path that has no network access to internal services or metadata in the first place.