URL Redirection Checker: Trace Every HTTP Hop, Diagnose Loops, and Verify Destinations
Learn how URL redirection checkers trace every HTTP hop, explain 3xx responses, find loops, and reveal SEO, security, and privacy issues.

A URL redirection checker follows a URL one response at a time and shows the complete path from the requested address to the final destination. For each hop, it should report the HTTP status, the Location header, the next URL, and whether the chain ends successfully, loops, or fails.
HTTP redirects are 3xx responses with a Location header. As MDN puts it, “Redirect responses have status codes that start with 3, and a Location header holding the URL to redirect to.” MDN explains the protocol details.
This guide shows how to check redirects from the command line, Python, and Node.js; how to read every hop; why chains affect performance and search; and what a redirect trace can and cannot tell you about safety and privacy.
What is a URL redirection checker?
A URL redirection checker is an inspection tool that requests a starting URL, receives its response, follows the indicated destination, and repeats until it reaches a final response or an error. A useful result looks like this:
- Requested URL: the exact address you submitted, including
httporhttps, path, query string, and fragment where applicable. - Hop 1: status code, response headers, and the destination from
Location. - Intermediate hops: each subsequent status and destination, in order.
- Final result: the last response, final URL, content status, loop, timeout, or invalid target.
Redirects are often intentional: a site may send HTTP traffic to HTTPS, map an old page to a replacement, or route a short link through a tracking service. They can also be accidental. A misconfigured rule can send two URLs back and forth, point to a deleted page, or add unnecessary hops to every visit.
How HTTP redirection works
The client makes a request. The server responds with a 3xx status and a Location value. The client then makes another request to that location. The process continues until a non-redirect response arrives or the client stops following.

| Status | Typical meaning | Method behavior |
|---|---|---|
| 301 | Moved permanently | Clients may change a non-GET request to GET. |
| 302 | Found; commonly temporary | Clients may change a non-GET request to GET. |
| 303 | See another resource | Common after POST so a reload does not repeat the submission. |
| 307 | Temporary redirect | Preserves the request method and body. |
| 308 | Permanent redirect | Preserves the request method and body. |
The status alone is not a diagnosis. A 301 ending at the correct page is usually healthy; a 301 that points to an unrelated domain is not. A 200 response at the end confirms that the destination answered, but it does not prove that the page is the one you intended.
HTTP headers versus browser-side navigation
Most command-line checkers follow response headers. A page can also navigate with JavaScript, an HTML meta refresh, or a script that changes window.location after the document loads. A header-only trace will not necessarily show those browser actions. If your users see a second navigation in a real browser, use a browser automation trace as well as an HTTP trace.
Check a redirect chain with cURL
Use -I to request headers and -L to follow redirects. The verbose form prints each request and response, which makes every hop visible.
curl -sS -D - -o /dev/null -L --max-redirs 20 https://example.com/old-page
For a compact final URL and status, ask cURL to print its variables after following the chain:
curl -sS -L -o /dev/null -w 'final=%{url_effective}\nstatus=%{http_code}\nredirects=%{num_redirects}\ntime=%{time_total}s\n' https://example.com/old-page
To inspect only the first response without following it:
curl -sS -I https://example.com/old-page
Look for a 3xx status and a Location header. Repeat the request against that location if you need a manually annotated trace. Some servers return different redirects for different methods, so a HEAD request can differ from a GET. When accuracy matters, use GET with a discarded body:
curl -sS -D - -o /dev/null https://example.com/old-page
Trace redirects with Python
Python’s Requests library follows redirects by default. The response keeps the complete history, allowing you to print every hop and the final result.
import requests
url = 'https://example.com/old-page'
response = requests.get(url, allow_redirects=True, timeout=30)
print('Requested:', url)
for number, hop in enumerate(response.history, start=1):
print(f'{number}. {hop.status_code} {hop.url}')
print(' Location:', hop.headers.get('Location'))
print('Final:', response.status_code, response.url)
Set allow_redirects=False when you need to inspect one response at a time, for example while debugging a loop or checking whether a server emits an absolute or relative location.
import requests
current = 'https://example.com/old-page'
seen = set()
for number in range(20):
if current in seen:
raise RuntimeError(f'redirect loop detected at {current}')
seen.add(current)
response = requests.get(current, allow_redirects=False, timeout=30)
print(number + 1, response.status_code, current)
if response.status_code not in {301, 302, 303, 307, 308}:
print('Final response:', response.status_code)
break
location = response.headers.get('Location')
if not location:
raise RuntimeError('redirect response has no Location header')
current = requests.compat.urljoin(current, location)
else:
raise RuntimeError('more than 20 redirects')
Trace redirects with Node.js
Modern Node.js fetch follows redirects by default. Set redirect: 'manual' to inspect one hop, or keep the default and use the final response URL.
const start = 'https://example.com/old-page';
const res = await fetch(start, { redirect: 'follow' });
console.log('Final:', res.status, res.url);
For a complete chain, request each hop manually and resolve relative locations with the standard URL class.
const redirectCodes = new Set([301, 302, 303, 307, 308]);
let current = 'https://example.com/old-page';
const seen = new Set();
for (let hop = 1; hop <= 20; hop++) {
if (seen.has(current)) throw new Error(`redirect loop at ${current}`);
seen.add(current);
const res = await fetch(current, { redirect: 'manual' });
console.log(`${hop}. ${res.status} ${current}`);
if (!redirectCodes.has(res.status)) {
console.log('Final URL:', current);
break;
}
const location = res.headers.get('location');
if (!location) throw new Error('redirect has no Location header');
current = new URL(location, current).href;
}
How to read a redirect checker result
1. Confirm the exact starting URL
Check the scheme, hostname, trailing slash, capitalization, query parameters, and port. http://example.com and https://example.com/ can legitimately produce different first responses. A query parameter may also select a regional, logged-in, or campaign destination.
2. Read each status and destination in order
Record the response code and the exact Location target. Relative values such as /new-page are resolved against the current URL. An external hostname, a tracking domain, or a change from HTTPS back to HTTP deserves specific attention.
3. Classify the ending
- Successful final response: the chain ends with a normal response such as 200, 204, or an expected 401/403.
- Broken destination: the final response is 404, 410, 500, or another error.
- Loop: a URL repeats, or the client reaches its redirect limit.
- Missing location: a 3xx response does not provide a usable destination.
- Unexpected host: the chain leaves the domains you expected.
Redirects, SEO, and site migrations
Search engines treat permanent and temporary redirects differently. Google describes 301 and 308 redirects as signals that the destination should replace the source in search results, while temporary redirects generally preserve the source URL. This is a signal, not a guarantee of rankings or immediate indexing. See Google’s site-move guidance.
Google Search Central says Googlebot can follow up to 10 hops in a redirect chain and still recommends sending users and crawlers directly to the final destination. Do not use that figure as a target. Update internal links, canonical URLs, sitemaps, feeds, and campaign links so they point directly to the final address.
During a migration, build a source-to-destination map, test representative URLs, and monitor crawl errors. A checker helps confirm the wire behavior, but it cannot decide whether the destination content is an appropriate replacement.
Performance: how many redirects are too many?
Every hop adds another request, DNS or connection work, TLS negotiation when the host changes, and server processing. On a fast local connection the delay may be small; on a mobile connection or a cross-region path it becomes more visible. There is no universal safe number, but a direct response is preferable whenever you control the link or rule.
- Fix internal links that point to old URLs.
- Collapse sequential rules, such as HTTP → old HTTPS host → new HTTPS host, into one direct redirect.
- Keep campaign and affiliate parameters while removing obsolete intermediate tracking where policy allows.
- Test cold and warm requests; caches can hide origin latency.
For bulk audits, cap concurrency, set timeouts, record timestamps, and retry transient network failures with backoff. Do not retry a deterministic 404 or a loop.
Security and privacy limits
An open redirect occurs when an application accepts untrusted input that controls the destination. An attacker can place a trusted domain in a link that ultimately sends visitors elsewhere. OWASP documents this as an unvalidated redirect or forward vulnerability in its Code Review Guide.

A trace can reveal that a link passes through a tracker or an unexpected domain. MDN describes redirect tracking as a pattern in which a brief visit to a tracker enables first-party storage and cross-site tracking; see MDN’s redirect-tracking guide. The path alone does not reveal exactly what data was collected.
Never treat a redirect checker as a malware, phishing, or reputation verdict. Use a separate security service, inspect the final domain, avoid submitting private URLs to third-party tools, and remember that a server can vary its response by IP address, user agent, cookies, region, or time.
Troubleshooting common redirect problems
| Symptom | Likely cause | Fix |
|---|---|---|
| “Too many redirects” | Two rules point at each other, often HTTP/HTTPS or www/non-www. | Compare the exact host and scheme at every hop; make one canonical target and exclude it from the source rule. |
| Browser works, script fails | JavaScript, cookies, authentication, or a user-agent challenge changes the flow. | Capture headers and browser navigation separately; reproduce required headers and cookies only in an authorized environment. |
| 301 appears where 307 was expected | A proxy, framework, or CDN rewrites the response. | Inspect the origin and each proxy layer; verify method-preservation requirements. |
| Relative Location cannot be parsed | The client treats it as an absolute URL. | Resolve it against the current URL, as new URL(location, current) does in Node.js. |
| Final status is 200 but page is wrong | A catch-all route or soft 404 serves generic content. | Compare title, canonical URL, body markers, and expected host; do not rely on status alone. |
| Different results from different tools | Different methods, headers, locations, cookies, or cache states. | Align request method and headers, record timestamps, and compare raw response headers. |
Or skip the browser setup
If your goal is to inspect the final page visually after tracing its URL, ScreenshotNeo can capture the rendered destination with one request. It is a screenshot API and MCP server; it does not replace an HTTP redirect trace, but it is useful for confirming what the final page looks like.
See the ScreenshotNeo API documentation for all options. This basic call returns an image:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account to get started.
Reliability and cost checklist
- Store the starting URL, every hop, status, location, final URL, and timestamp.
- Set a maximum hop count and a total timeout.
- Detect repeated URLs before making another request.
- Respect authorization, robots policies, and privacy requirements for URLs you do not own.
- Use GET when you need to model a normal page visit; test non-GET methods separately.
- For recurring audits, cache results briefly but recheck after DNS, CDN, or redirect-rule changes.
FAQ
Can a redirect checker tell me whether a link is safe?
No. It shows observed destinations and intermediate domains. It does not certify that a destination is free of malware, phishing, or unwanted data collection.
Does a 301 always improve SEO?
No. It is a permanent-move signal. The destination still needs to be relevant, accessible, and properly processed by search engines.
Why does a checker show fewer hops than my browser?
The browser may execute JavaScript, follow a meta refresh, send cookies, or receive a different response based on its user agent or location.
Should I remove every redirect?
No. Redirects are appropriate for HTTPS enforcement, domain changes, deleted pages with replacements, and other intentional moves. Remove unnecessary hops and loops.
Can redirects change request data?
Yes. 307 and 308 preserve the method and body, while clients may convert a POST to GET after 301 or 302. Use 303 when you intentionally want a separate retrieval request.


