How link shorteners and redirect chains hide where a link goes
How HTTP redirects, meta refresh, JavaScript and shorteners hide a link's destination, with hop counts we measured on 23 public URLs.
Updated 2026-10-108 sources
On this page
A redirect chain is the series of responses a link passes through before it reaches a final page, and the address a person sees first says nothing about where the chain ends. In our measurement on 10 October 2026 [8], 14 of 17 well-known sites redirected a plain http:// request at least once, and 6 of them took two redirects; a walker that reads only status codes cannot see redirects a page triggers through HTML or JavaScript.
Key facts#
- RFC 9110 defines five redirect status codes that carry a
Locationheader: 301, 302, 303, 307 and 308 [1]. # - RFC 9110 also says a client “SHOULD detect and intervene in cyclical redirections” and notes that an earlier specification recommended a maximum of five redirections [1]. #
- A
<meta http-equiv="refresh">tag is a timed redirect defined in the HTML Standard, andlocation.assign()navigates from JavaScript [2][3]. Neither produces a 3xx status, so a tool that reads only status codes does not see them. # - On 10 October 2026, 14 of 17 well-known public sites we requested over
http://redirected at least once; the maximum was 2 redirects. # - OWASP names open redirects (an application that redirects to a URL supplied in a parameter) as a phishing risk because the link carries the trusted site’s name [4]. #
- Abnormal AI reported that 21.6% of the phishing emails it analysed in the second half of 2025 used redirect links, and 10.2% of those used link shorteners [7]. #
- As of October 2026, StormDoor’s links gate follows HTTP redirects only, up to 10 fetches, and does not parse page bodies. #
How does an HTTP redirect work?#
A server answers a request with a 3xx status and a Location header; the client repeats the request at the new address. RFC 9110 separates permanent redirects (301, 308) from temporary ones (302, 307) and says 307 and 308 were added to preserve the request method, because clients had converged on changing POST to GET for 301 and 302 [1]. A client following a chain applies the same rule at each hop, so the first address shown to a user can be several requests away from the page they land on.
The RFC sets no mandatory hop limit. Real clients choose their own: StormDoor’s gate documentation puts curl’s default at 50 and most browsers at around 20 (we did not re-verify those figures), so the same chain can resolve in one tool and fail in another. StormDoor’s own cap is lower than both (see below).
Why does a plain HTTP client miss some redirects?#
Two redirect mechanisms live in the page, not in the headers:
- Meta refresh. The HTML Standard describes
http-equiv="refresh"as a pragma that “acts as a timed redirect”, with a time in seconds and an optional URL [2]. - JavaScript.
Location.assign()“causes the window to load and display the document at the URL specified” [3]. Any script can call it, including after a delay, a user-agent test or an IP test.
The server’s status for such a page is 200 OK. We checked this on loopback with three local pages served from our own machine: the HTTP 302 page returned status 302 with a redirect URL; the meta-refresh page and the JavaScript page both returned status 200 with no redirect URL.
A tool that only reads status codes therefore reports a one-hop, clean-looking chain for those pages. Only a tool that fetches the body and runs or parses it, usually a headless browser, follows them.
What is an open redirect?#
An open redirect is an application feature that sends users to a URL taken from a request parameter without validating it. OWASP gives the pattern http://example.com/example.php?url=http://malicious.example.com: the link begins on a trusted domain and ends elsewhere, and OWASP lists phishing as the main risk [4]. Its recommended fixes are to avoid redirects, map IDs to destinations on the server, or allowlist parsed scheme, host, port and path [4].
For a screening tool, an open redirect on a reputable domain is a hop that looks harmless. Reputation checks on the first domain pass; only the full chain reveals the destination.
Two illustrative chains, stepped one hop at a time (example domains, not captured traffic):
- 301https://short.example/x7KpLocation: https://track.example/c?id=8812
The shortener: the only address the reader sees.
- 302https://track.example/c?id=8812Location: http://shop.example/autumn-sale
A click tracker logs the visit and passes it on.
- 301http://shop.example/autumn-saleLocation: https://shop.example/autumn-sale
The shop upgrades plain HTTP to HTTPS.
- 308https://shop.example/autumn-saleLocation: https://www.shop.example/autumn-sale
The shop moves to its preferred hostname.
- 200https://www.shop.example/autumn-sale
The page that loads. Four redirects, three different domains.
- 301https://short.example/Qm3vLocation: https://news.trusted.example/out?to=https%3A%2F%2Fsecure-login.example%2Fverify
The shortener hides the real target.
- 302https://news.trusted.example/out?to=…Location: https://secure-login.example/verify
An open redirect on a site people trust forwards to whatever address it is given.
- https://secure-login.example/verify<meta http-equiv="refresh" content="0;url=https://acc0unt-check.example/signin">
The server answers 200, but the page tells the browser to move on immediately.
A checker that only follows status codes reports this page as the destination.
- 200https://acc0unt-check.example/signin
The page that actually loads, and asks for a password.
All domains are reserved example domains (.example, RFC 2606). The chains illustrate common patterns; they were not captured from live traffic.
What do link shorteners add?#
A shortener maps a short code to a long URL and answers with a redirect, so the visible text never shows the destination. Their policies matter because they decide what happens when a short link is bad or old:
- Bitly says it calls its Abuse API while decoding a link to check whether the destination is malicious. A link in the “warn” category shows an interstitial with the long URL; in the “blocked” category the interstitial hides the long URL [6] (post dated 21 February 2023).
- Google’s goo.gl stopped accepting new URLs in 2018. Google announced on 18 July 2024 that goo.gl links would stop responding after 25 August 2025, then updated the post on 1 August 2025 to say only links with no recent activity would be deactivated and all others would keep working [5]. A short link is only as durable as its operator.
Chaining two shorteners puts the destination behind two operators, each of which sees only its own hop. StormDoor flags two or more distinct shortener services in one chain (link.shortener_chain).
How often do phishing emails use redirects?#
Abnormal AI, an email-security vendor, analysed nearly 800,000 email attacks across more than 4,600 organizations in the second half of 2025 and reported that 21.6% of phishing attacks used redirect links, with 10.2% of those relying on link shorteners. Abnormal calls redirect chains “a deliberate evasion technique” [7]. This is one vendor’s customer sample, published 29 April 2026, and the page gives no further method.
What do redirect chains look like on ordinary sites?#
We walked 23 public URLs one hop at a time with curl on 10 October 2026, recording the status code at each hop (method in the Method section). The first group is 17 well-known sites and shortener home pages requested over http://.
| Start URL | Redirects | Status sequence | Final URL |
|---|---|---|---|
http://google.com |
1 | 301 > 200 | http://www.google.com/ |
http://www.google.com |
0 | 200 | http://www.google.com |
http://github.com |
1 | 301 > 200 | https://github.com/ |
http://www.github.com |
2 | 301 > 301 > 200 | https://github.com/ |
http://wikipedia.org |
2 | 301 > 301 > 200 | https://www.wikipedia.org/ |
http://en.wikipedia.org/wiki/Main_Page |
1 | 301 > 200 | https://en.wikipedia.org/wiki/Main_Page |
http://amazon.com |
2 | 301 > 301 > 200 | https://www.amazon.com/ |
http://www.bbc.com |
1 | 302 > 200 | https://www.bbc.com/ |
http://bbc.co.uk |
2 | 301 > 301 > 200 | https://www.bbc.co.uk/ |
http://youtube.com |
2 | 301 > 301 > 200 | https://www.youtube.com/ |
http://reddit.com |
2 | 301 > 301 > 403 | https://www.reddit.com/ |
http://example.com |
0 | 200 | http://example.com |
http://www.mozilla.org |
1 | 301 > 200 | https://www.mozilla.org/ |
http://bit.ly |
1 | 302 > 200 | https://bitly.com/ |
http://tinyurl.com |
1 | 301 > 200 | https://tinyurl.com/ |
http://t.co |
0 | 520 | http://t.co |
http://goo.gl |
1 | 301 > 400 | https://goo.gl/ |
Of these 17, 3 had no redirect, 8 had one and 6 had two. The first hop was 301 in 12 cases and 302 in 2 cases. t.co answered 520 and reddit.com ended in 403 at the last hop, which we believe is bot filtering but did not verify; goo.gl ended in 400 on its root page. We did not create any shortener links.
The second group is httpbin.org’s documented /redirect/N endpoints, which return N redirects:
| Start URL | Redirects | Status sequence | Final URL |
|---|---|---|---|
https://httpbin.org/redirect/1 |
1 | 302 > 200 | https://httpbin.org/get |
https://httpbin.org/redirect/3 |
3 | 302 > 302 > 302 > 200 | https://httpbin.org/get |
https://httpbin.org/redirect/10 |
10 | 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 200 | https://httpbin.org/get |
https://httpbin.org/redirect/12 |
12 | 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 302 > 200 | https://httpbin.org/get |
https://httpbin.org/status/301 |
2 | 301 > 302 > 200 | https://httpbin.org/get |
https://httpbin.org/absolute-redirect/3 |
3 | 302 > 302 > 302 > 200 | http://httpbin.org/get |
Two points follow. Ordinary sites chain two hops for http:// plus a www canonicalisation, so “more than one hop” alone is not suspicious. And /redirect/10 and /redirect/12 resolved in our 20-hop walk, yet would exceed a 10-fetch cap.
What does StormDoor’s links gate do, and what are its limits?#
POST /v1/links follows a URL’s redirect chain, then checks the final domain’s registration age and typosquat signals, and flags known shorteners (gate docs). You can try the same chain walk in the free redirect checker and the link checker. StormDoor makes both tools.
From the gate’s implementation and its documentation:
- Status codes only. It follows 301, 302, 303, 307 and 308 and never reads a page body. Meta-refresh and JavaScript redirects are not followed, so a chain that continues that way ends at the page that contains it. The gate also does no content scanning of the destination.
- 10 fetches. The cap is 10 hops. By our reading of the loop, a chain of up to 9 redirects resolves and one with 10 or more is cut off, which emits link.hop_cap_exceeded. We did not run this against a live chain.
- 8-second budget for the redirect-chain check.
- HEAD first. Each hop uses
HEAD, falling back toGETonly on 405 or 501. A server can answer HEAD and GET differently. - Unsafe hops are refused. A redirect to a non-HTTP(S) scheme or a non-public host stops the walk and emits link.unsafe_redirect_target. The docs state this per-hop validation does not fully close DNS rebinding.
- One vantage point. The gate cannot see cloaking that depends on the visitor’s user agent, referrer or IP. Its docs note that long chains are a common cloaking technique.
A browser-based tool will catch JavaScript redirects that this gate misses; a status-code walker is faster and cheaper, and it will not execute attacker-controlled script.
Method#
- Date and host. All measurements on 10 October 2026 from one server, using curl with
--max-redirs 0per hop, a 15-second timeout and a hop cap of 20. HEAD first, GET on 405 or 501, as the gate does. - Sample. 23 URLs: 17 chosen by hand (major sites and shortener home pages), plus 6 httpbin.org endpoints. This is a convenience sample, not representative of the web.
- Counted. Redirect count is the number of 3xx responses with a
Locationbefore a non-redirect response. - Not done. No malicious URLs were visited. No browser was used, so JavaScript and meta-refresh behaviour on live sites is not measured. Results depend on location and user agent and can change.
- Local demo. Three pages served on loopback (an HTTP 302, a meta-refresh page and a JavaScript
location.assignpage) were requested with curl on 10 October 2026; the status and any redirect URL curl reported were recorded. - Limits. One run, one vantage point, no repeats, so a site’s answer on another day or from another network may differ.
- Not verified. The common claim that appending
+to a Bitly link previews its destination: we found it only on third-party pages and not in Bitly’s documentation, so we left it out.
Sources#
- IETF, RFC 9110 section 15.4, accessed 2026-10-10.
- WHATWG, HTML Standard, refresh pragma, accessed 2026-10-10.
- MDN, Location.assign(), accessed 2026-10-10.
- OWASP, Unvalidated Redirects and Forwards Cheat Sheet, accessed 2026-10-10.
- Google Developers Blog, Google URL Shortener links will no longer be available, accessed 2026-10-10.
- Bitly, Trust and Safety: An Overview of Our Abuse System, 21 February 2023, accessed 2026-10-10.
- Abnormal AI, 2026 attack landscape report: phishing, 29 April 2026, accessed 2026-10-10.
- StormDoor internal measurement, 2026-10-10; method described in this article.
Redirect checker
Hear when StormDoor opens
StormDoor is not open for signups yet. Join the waitlist and we will email you when it is.
Check your inbox
We sent a confirmation link to the address you entered. Click it to join the list. It can take a minute, and it may land in spam.