There is no single switch that stops (server-side request forgery). The defenses that work are layered, and they share one theme: decide based on the real destination, not the text of the URL.
Prefer an allow-list
If the feature only needs to reach a known set of hosts, allow exactly those and reject everything else. An allow-list fails safe: a spelling the attacker invents is simply not on the list. A block-list fails open, because it can only stop the internal addresses and forms you thought to list, and the obfuscation tricks give an attacker endless new spellings.
Resolve, then check, then pin
If you must accept arbitrary URLs, resolve the hostname to an IP, classify that IP against the internal ranges, and reject internal results. Then use the address you validated for the actual connection. Checking the name and then connecting by name again opens a time-of-check to time-of-use gap called DNS rebinding, where the name resolves to a safe address during validation and to an internal one a moment later. Validate and connect to the same resolved address.
Handle redirects and schemes
A safe first URL can redirect to http://169.254.169.254/. Either disable redirects on the fetch, or re-run the full classification on every hop. Restrict the scheme to http and https so that file://, gopher://, and similar cannot be used to escalate.
Add network-layer controls
Defense in depth means not relying on the application alone. Block outbound traffic from the fetching service to internal ranges and to the metadata address at the network or firewall layer, so a logic bug does not become a breach. On , enforce IMDSv2. The application-layer classifier, the resolve-then-check step, and the egress rules each catch what the others miss.
The allow-list that validates a name and connects to an address
The defence most often defeated is the one that looks correct: resolve the host, check the address against the policy, then make the request. Two lookups happen, and nothing guarantees they return the same answer.
That gap is DNS rebinding. An attacker controls a name with a very short ; the validating lookup returns a permitted public address, and the connecting lookup, moments later, returns 169.254.169.254. Every line of the check ran correctly and the request went somewhere the policy forbids.
Closing it means checking and connecting against the same resolved address — resolve once, validate that address, then connect to it rather than to the name.
And redirects re-open it. A permitted host answering 302 to an internal URL bypasses a check performed only on the original input. Either follow no redirects, or re-validate every hop, because a policy applied once to a chain of requests is a policy applied to the first one.