How to Trace Redirects with cURL and Command Line

cURL commands for tracing redirects: follow redirect chains, view HTTP status codes, inspect response headers, and script bulk redirect checks. Copy-paste commands included.

cURL is the single best tool for tracing redirects. It shows you exactly what happens at the HTTP level — no browser cache, no cookies, no JavaScript interference. Just raw requests and responses.

Here is every cURL command you need for redirect debugging, from simple one-liners to scripted bulk checks.

The Essential Command

curl -sIL "https://example.com/old-page"

This follows the entire redirect chain and prints every response header. The flags:

  • -s — silent mode, suppresses the progress bar
  • -I — HEAD request, fetches headers only (no body)
  • -L — follow Location headers (follow redirects)

The output shows each hop:

HTTP/2 301
location: https://www.example.com/old-page

HTTP/2 301
location: https://www.example.com/new-page

HTTP/2 200
content-type: text/html

Two redirects, then a 200. That is a redirect chain — and it should probably be flattened to one hop.

Extract Just the Status Codes and Locations

Full headers are noisy. Filter to only the status lines and Location headers:

curl -sIL "https://example.com/old-page" 2>&1 | grep -iE "^(HTTP/|location:)"

Output:

HTTP/2 301
location: https://www.example.com/new-page
HTTP/2 200

Clean, scannable, and easy to pipe into other tools.

Measure Redirect Timing

Every redirect hop costs time. Measure it:

curl -sIL -o /dev/null -w "redirects: %{num_redirects}\ntotal_time: %{time_total}s\nfinal_url: %{url_effective}\nfinal_code: %{http_code}\n" "https://example.com/old-page"

Output:

redirects: 2
total_time: 0.847s
final_url: https://www.example.com/new-page
final_code: 200

Time per redirect

For detailed per-hop timing, use --write-out with %{time_redirect} to see just the time spent on redirects, separate from the final response.

Follow Redirects with a GET Request

Some servers behave differently for HEAD (-I) vs GET requests. If you suspect this, use GET but discard the body:

curl -sL -o /dev/null -D - "https://example.com/old-page"
  • -D - dumps headers to stdout
  • -o /dev/null discards the response body

This sends a full GET request at each hop but still shows you all headers.

Limit the Number of Redirects

Prevent infinite loops from hanging your terminal:

curl -sIL --max-redirs 10 "https://example.com/old-page"

The default is 50 redirects. For debugging, 10 is more than enough — if a chain exceeds 10 hops, something is very wrong.

Redirect loops

If cURL exits with curl: (47) Maximum redirects followed, you have a redirect loop. Use -v (verbose) to see the loop pattern and identify which URLs are cycling.

Verbose Mode for Full Debugging

When you need everything — request headers, response headers, TLS handshake details:

curl -vL "https://example.com/old-page" 2>&1 | grep -E "^[<>*]"

Lines starting with > are sent by cURL (request). Lines starting with < are received from the server (response). Lines starting with * are connection info.

Send Custom Headers

Test how redirects behave with specific user agents or cookies:

# Test as Googlebot
curl -sIL -A "Googlebot/2.1" "https://example.com/old-page"

# Test with a specific cookie
curl -sIL -b "session=abc123" "https://example.com/old-page"

# Test with a custom header
curl -sIL -H "X-Forwarded-Proto: https" "https://example.com/old-page"

User-agent matters

Some sites redirect differently based on user agent — mobile vs desktop, bot vs human. Test with multiple user agents if you suspect this is happening.

Trace your redirect chains

Find redirect loops, broken chains, and unnecessary hops instantly.

Bulk Redirect Checking with cURL

Check a list of URLs from a file:

while IFS= read -r url; do
  result=$(curl -sIL -o /dev/null -w "%{http_code} %{num_redirects} %{url_effective}" "$url")
  echo "$url -> $result"
done < urls.txt

For a TSV-formatted report:

echo -e "original_url\tfinal_code\tredirects\tfinal_url"
while IFS= read -r url; do
  curl -sIL -o /dev/null \
    -w "$url\t%{http_code}\t%{num_redirects}\t%{url_effective}\n" \
    "$url"
done < urls.txt

Speed Up Bulk Checks with xargs

Process multiple URLs in parallel:

cat urls.txt | xargs -P 10 -I {} curl -sIL -o /dev/null \
  -w "{} -> %{http_code} (%{num_redirects} redirects) -> %{url_effective}\n" "{}"

-P 10 runs 10 requests concurrently. Adjust based on your connection and the target server's tolerance.

Compare Expected vs Actual Redirects

Create a CSV of expected redirects and validate them:

# expected.csv format: source_url,expected_destination
while IFS=, read -r source expected; do
  actual=$(curl -sIL -o /dev/null -w "%{url_effective}" "$source")
  if [ "$actual" = "$expected" ]; then
    echo "PASS: $source -> $actual"
  else
    echo "FAIL: $source -> $actual (expected: $expected)"
  fi
done < expected.csv

This is invaluable after site migrations. Export your redirect map, run this script, and catch every broken redirect before your users do.

cURL Flags Quick Reference

FlagPurposeExample
-LFollow redirectscurl -L url
-IHEAD request (headers only)curl -IL url
-sSilent (no progress)curl -sL url
-vVerbose outputcurl -vL url
-o /dev/nullDiscard bodycurl -Lo /dev/null url
-w FORMATCustom output formatcurl -w '%{http_code}' url
-D -Dump headers to stdoutcurl -LD - url
--max-redirs NLimit redirect hopscurl -L --max-redirs 5 url
-A AGENTSet user-agentcurl -LA 'Googlebot' url

cURL does not lie. It shows you exactly what the server sends, with no browser magic in between.

Never miss a broken redirect

Trace redirect chains and detect issues before they affect your users and SEO. Free instant tracing.