Definition
When you build a feature where the server "sends a request on the user's behalf to an address they gave" (image preview, link thumbnail, etc.), this is the method for blocking an attack where the attacker swaps that address for an internal server-side address to sneakily reach it.
Let's unpack the terms first: fetch is the server sending an HTTP request to some URL and retrieving the content; an allowlist is a whitelist of "allow requests only to these hosts"; and a scheme is the protocol type at the very front of a URL, such as http:, https:, or file:.
To be precise, SSRF (Server-Side Request Forgery) is a vulnerability where the attacker makes the server fetch an unintended URL. Server endpoints that take a ?url= parameter and request that address—an image-resize proxy, an OG fetcher (a server that fetches a target page's metadata for link-share previews), a webhook relay (a server that forwards external event notifications on your behalf)—are all exposed to this risk.
Why It's Needed
A browser (client) can't send requests directly into the company's internal network from outside, but the server can. The server can also reach the internal network, private IPs, and the cloud metadata address (a special internal address where the cloud responds with the instance's own credentials/config; 169.254.169.254 for AWS).
So if you let a client hit an address it can't directly reach by way of the server proxy, an attacker can use the server as a tool for internal data exfiltration and internal-network port scanning. The mere assumption "we only accept public image URLs" doesn't block this—you have to verify that the received URL really is one.
How It Works
- Parse the URL → check that the scheme is
http:/https:. - Hostname allowlist — suffix matching (e.g.,
.cdn.example.com,.storage.provider) or an explicit host list. - On rejection, 400 + include the rejected hostname in the body (operational debugging).
- (Recommended) block private IP / link-local / metadata IP, re-verify the host at each redirect hop.
- Add per-deployment suffixes via environment variables — allow a new CDN domain without code changes.
Practical Application
- Image proxy: let only allowed storage/CDN suffixes through; perform the fetch with the full presigned URL (signature included), but separate concerns so the cache key uses only the pathname.
- Error response:
{ error: "Host not allowed", host: "evil.example" }— immediately confirm an allowlist omission. - Deployment checklist: when adding a new object storage domain, update env + allowlist at the same time.
Tradeoffs
| Choice | Benefit | Cost |
|---|---|---|
| Suffix allowlist | Simple ops | Need to design against subdomain bypass patterns |
| Denylist only | Fast to adopt | Vulnerable to bypass / redirects |
| Redirects disabled | Safe | Some CDN behavior may break |
When Not to Use It
- Allowing arbitrary user-URL fetches without an allowlist "for convenience."
- Keeping the allowlist opened up to
*(allow all hosts) for internal debugging as-is in production.
Common Mistakes
- You added the host in code, but an old production build (
next start) is still running, so it isn't reflected — check the process start time / command. - Confusing a presigned URL's signature query with host verification — the host is the allowlist, the fetch URL is the full one.
- A suffix-matching bug like
evil.amazonaws.com.attacker.com— review thehost.endsWith('.amazonaws.com')rule.
Related Concepts
- presigned-url-direct-upload — client direct upload vs. server fetch
- server-image-proxy-transcoding-cache — applying the guard in an image proxy