Tracing Short URLs and Redirect Chains

Follow URL shorteners and redirect chains programmatically. Resolve bit.ly, t.co, and custom redirects to their final destinations in JavaScript, Python, and curl.

ZERO UPLOAD · ALL LOCAL
  1. Paste any URL into the input box — results appear instantly as you type.
  2. The URL Parts section shows all 17 URL components: scheme, host, pathname, and more.
  3. If the URL has query parameters, they appear in the Query Parameters section below.
  4. Click the circle-? icon next to a known parameter name to see what it does.
  5. Use the Copy buttons next to each property or parameter to grab individual values.

Worked examples for this use case

Follow redirect chain in Python, parsing each Location header

import requests from urllib.parse import urljoin def resolve_url(start_url: str, max_hops: int = 20) -> list[str]: chain = [start_url] url = start_url session = requests.Session() for _ in range(max_hops): resp = session.get(url, allow_redirects=False, timeout=5) if resp.status_code not in (301, 302, 303, 307, 308): break location = resp.headers.get("Location", "") url = urljoin(url, location) # resolve relative Location headers chain.append(url) return chain

Resolve a short URL with curl (command line)

# -L follows redirects, -I fetches headers only, -s is silent curl -LIs "https://bit.ly/example" | grep -i "location:" # Or use --max-redirs to cap hops and see the final URL curl -Ls -o /dev/null -w "%{url_effective}" --max-redirs 20 "https://bit.ly/example"

URL PARTS

href
scheme
protocol
origin
authority
host
hostname
subdomain *
domain *
tld *
port
resource
directory
pathname
filename
search
hash

* Subdomain, domain, and TLD use simple dot-splitting and may be incorrect for two-part TLDs (co.uk, com.au).

QUERY PARAMETERS

Tracing Short URLs and Redirect Chains

URL shorteners convert long URLs to short aliases that redirect visitors to the original destination. Following a short URL to its final destination by resolving the full redirect chain is a common task in link auditing, spam detection, analytics pipelines, and security tools. A single short URL may pass through multiple redirect hops before reaching the final page.

Resolving redirect chains requires making HTTP requests rather than URL parsing alone, but parsing the Location header at each hop is where URL handling comes in. Each redirect produces a Location header containing the next URL, which may be absolute or relative to the current URL1. Correctly resolving relative Location headers is the most common source of bugs in redirect-following code.

Following redirects programmatically

Most HTTP libraries follow redirects automatically: fetch() and axios in JavaScript, requests in Python, and curl at the command line all follow 3xx responses by default. To inspect each hop, disable automatic redirect following and handle each response manually. In Python: response = requests.get(url, allow_redirects=False). A 301, 302, 303, 307, or 308 response carries the next URL in the Location header1. Building on this, always validate the response status code before reading Location because a missing header raises a KeyError in most libraries.

Reading and resolving the Location header at each hop

Consequently, read Location with response.headers['Location'] and resolve it against the current URL using urljoin(current_url, location_header)2. Some servers return relative Location values that require resolution, so always run the resolved URL through a parser before issuing the next request. A relative Location of ../new against a base of /docs/guide/ resolves to /docs/new, while the same Location against /docs/guide resolves to /new because the base directory differs. Testing your resolver with both trailing-slash and no-trailing-slash base URLs catches the most common redirect-following bugs.

Parsing the Location header

RFC 7231 specifies that the Location header value is a URI reference, which may be absolute or relative3. Building on this, most browsers and libraries treat it as absolute because poorly-configured servers often return invalid relative Location values. For robust handling, resolve the Location value against the request URL using a standards-compliant resolver: new URL(location, requestUrl) in JavaScript, or urljoin(requestUrl, location) in Python. A chain ends when the response status is 200 or when a configurable hop limit is reached, since infinite redirect loops do occur and must be guarded against with both a counter and a visited-URL set.

Privacy and security considerations

Resolving short URLs reveals the final destination, which is useful for safety but also a privacy consideration when the short URL contains sensitive query parameters. Some URL shorteners preserve tracking parameters through the redirect chain; others strip them. Consequently, comparing the resolved final URL to a known-safe allowlist prevents open redirect attacks where a short URL leads to a malicious site4. For automated pipelines, set a maximum hop count (typically 10–20), a timeout per hop, and a list of blocked domains. Building on this, parse the final URL's hostname and path before storing or displaying it, and avoid trusting the short URL provider's preview API for security decisions because the preview endpoint may return stale data.

Resolving redirect chains with HEAD requests

A HEAD request resolves a redirect chain without downloading the body, making it more efficient than GET for tracing short URLs. In Python: requests.head(url, allow_redirects=False, timeout=5) returns response headers including Location without fetching the page content. In Node.js, fetch(url, {method: 'HEAD', redirect: 'manual'})5 achieves the same result. Use HEAD when you only need the final URL, not the page content. Some servers do not respond to HEAD requests correctly (returning 405 Method Not Allowed); fall back to GET with a small max-redirects count for those cases.

Detecting redirect loops and circular references

Redirect loops occur when URL A redirects to URL B and URL B redirects back to URL A (or a longer cycle). Following redirects without a hop limit causes infinite loops that hang your application. Set a maximum hop count (10-20 is standard) and track visited URLs in a set. Before following a redirect, check if the target URL is already in the visited set; if so, a loop has been detected and you should stop. In Python, the requests library follows redirects by default but limits to 30 hops6; in Node.js, fetch follows up to 20 hops. For manual redirect handling, implement your own counter and visited-set logic.

Beyond cycles, also guard against the same visited-URL check catching a slow convergence that bounces between several URLs without ever repeating one immediately. A frontier set of pending URLs plus an upper bound on total redirects covers both cases without much extra code. When a loop is detected, surface the full chain you collected so the duplicate endpoint is easy to spot, because the loop start point is what you report to whoever owns the redirect configuration.

Short URL analytics and click tracking

URL shortening services (bit.ly, tinyurl.com) provide click analytics through their APIs, but these metrics are limited to clicks on the short URL itself. If the short URL is shared and clicked from a context where the original link is visible (email signatures, printed QR codes), clicks on the original destination are invisible to the shortener's analytics. For comprehensive attribution, add UTM parameters to the destination URL before shortening: shorten('https://example.com/landing?utm_source=banner&utm_medium=email'). The UTM parameters survive the redirect and appear in your analytics platform, giving you attribution data regardless of how the user arrived at the destination.

Building your own URL shortener with redirect tracking

For applications that need per-user click tracking, building a custom URL shortener gives you full control over the data. The architecture is straightforward: a database table mapping short codes to destination URLs and metadata (creator, creation date, campaign), and a redirect endpoint that looks up the short code, records the click (timestamp, user agent, IP), and issues a 302 redirect. For high-traffic shorteners, use an in-memory cache (Redis) for the code-to-URL mapping to avoid database lookups on every click. The 302 status is important: it tells clients and search engines that the redirect is temporary and the short URL remains the canonical address for the resource.

When to use this

Resolve short URLs and redirect chains when you want to see where a shortened bit.ly link actually leads, before building analytics pipelines that need final destination URLs, implementing link preview features, or deduplicating URLs in a crawler that encounters both short and long forms of the same resource.

Examples

Follow redirect chain in Python, parsing each Location header

import requests from urllib.parse import urljoin def resolve_url(start_url: str, max_hops: int = 20) -> list[str]: chain = [start_url] url = start_url session = requests.Session() for _ in range(max_hops): resp = session.get(url, allow_redirects=False, timeout=5) if resp.status_code not in (301, 302, 303, 307, 308): break location = resp.headers.get("Location", "") url = urljoin(url, location) # resolve relative Location headers chain.append(url) return chain

Resolve a short URL with curl (command line)

# -L follows redirects, -I fetches headers only, -s is silent curl -LIs "https://bit.ly/example" | grep -i "location:" # Or use --max-redirs to cap hops and see the final URL curl -Ls -o /dev/null -w "%{url_effective}" --max-redirs 20 "https://bit.ly/example"

Sources
  1. 1.

    R. Fielding and J. Reschke, "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content," RFC 7231, IETF, June 2014. https://www.rfc-editor.org/rfc/rfc7231.txt

  2. 2.

    Python Software Foundation, "urllib.parse — Parse URLs into components," docs.python.org, accessed June 2026. https://docs.python.org/3/library/urllib.parse.html

  3. 3.

    Mozilla Developer Network, "Location header — HTTP," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Location

  4. 4.

    OWASP Foundation, "Unvalidated Redirects and Forwards," cheatsheetseries.owasp.org, accessed June 2026. https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html

  5. 5.

    Mozilla Developer Network, "RequestInit — Web APIs," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/API/RequestInit

  6. 6.

    Requests library documentation, "requests.sessions," docs.python-requests.org, accessed June 2026. https://docs.python-requests.org/en/latest/_modules/requests/sessions/

FAQ