Tracing the source of a redirect can be difficult, especially for a redirect loop, but the curl command can make it much easier.
One of the most frustrating things to encounter is a redirect loop that you can’t trace back to it’s original, but if you plan your approaches well it can be very simple to maintain and fix.
Redirects Can Be Anywhere
Redirects can be set in many different ways, at almost every level of the load cycle for a website or page.
- JavaScript with
location.href = ... - HTML with
<meta http-equiv="refresh" content="0; url=http://example.com/" /> - Raw PHP with `header(‘Location: …’)
- WordPress PHP with
wp_redirect('...') - Apache server-level inside of .htaccess with
mod_aliasormod_rewriterules - Nginx server-level rules with
location,returnorrewrite - Host configured rules inside of control panels at hosts like WPEngine or Kinsta
- CloudFlare DNS level rules or worker processes
Needless to say it can be difficult to track down which one is the source of a redirect. But there are a few approach and tools you can use to make the process much easier.
Tracing Redirects
The first step to reverse tracing an HTTP redirect is to check the HTTP headers on the URL you’re having trouble with. To quickly see what status code and headers a URL returns you can use the -I option with the curl command.
curl -I https://kevinleary.net
This example redirects to www.kevinleary.net, so the response provided back in my Terminal looks like this:
HTTP/2 301
date: Fri, 31 Oct 2025 15:08:05 GMT
content-type: text/html
location: https://www.kevinleary.net/
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=Grz7MNeBpICNsvGqhibhPJ3z96JkrzsXpHcoaR7YHwhyC7LG1WAzCGEHsvAwELq1MRr%2BiEZQRV2VBi0hTDCnVUJrpB%2FzkY82UflR"}]}
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
cf-ray: 997410d679fc906b-BOS
cf-cache-status: MISS
server: cloudflare
x-xss-protection: 1; mode=block
strict-transport-security: max-age=15552000; includeSubDomains; preload
vary: Accept-Encoding
cf-apo-via: origin,resnok
expect-ct: max-age=86400, enforce
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
speculation-rules: "/cdn-cgi/speculation"
set-cookie: __cf_bm=j6FdKrn.ZT08WKU9qzAhiotIXa2mf3jCyKec9iU7Xjs-1761923285-1.0.1.1-9uRQ62OqOR6ElydfVBar27SivfUU7f_GHp.sUpIuR6D7ouSQajEwDZ6Ms8D8lD6WWedfT4NO8djrQJ6fuvXzGYQ8LD5iXwgHruGLjnV6FZE; HttpOnly; SameSite=None; Secure; Path=/; Domain=kevinleary.net; Expires=Fri, 31 Oct 2025 15:38:05 GMT
In this response the most important lines are:
HTTP/2 301identifies the page as a permanent redirect, anything beginning with3is also a redirect, there are various types other than permanentlocationtells us where a redirect is pointing
One missing line though is the X-Redirect-By, which is usually only available for PHP/server redirects. When it’s there it’s very useful because it’s basically a note/reminder/hint of where the redirect originated from.
For WordPress websites it will identify built-in WordPress redirects like those added with rel_canonical as:
Follow Redirects & Show Each Step
Follow redirects automatically and display each response along the way.
curl -IL https://example.com
Limit Redirects (Prevent Infinite Loops)
Stop following redirects after a specific number to detect loops.
curl -IL --max-redirs 5 https://example.com
Show Response Time for Each Redirect
Measure how long each redirect takes to complete.
curl -ILw "%{time_total}s - %{url_effective}\n" https://example.com
Verbose Redirect Tracing
See both the request and response headers for full transparency.
curl -ILv https://example.com
Basic Redirect Chain Analysis
Display the entire redirect path from start to finish.
curl -IL https://example.com/old-page
Complete Request/Response Flow
Save the detailed trace of all redirects and responses to a log file.
curl -ILv --trace-ascii trace.log https://example.com
Custom Headers to Identify Requests
Add a custom header (like a fake user agent) to help identify your test requests.
curl -IL -H "User-Agent: Redirect-Tracer" https://example.com
Check Specific HTTP Methods
Simulate different types of requests like POST or GET to see how they’re redirected.
curl -IL -X POST https://example.com
WordPress Redirects
Detect if WordPress is redirecting a post or page due to permalink or canonical changes.
curl -IL https://yoursite.com/sample-page
Server-Level Redirects (Nginx/Apache)
Identify redirects caused by web server configuration, not the app.
curl -IL https://example.com
Infinite Redirect Loop
Catch redirect loops where a site keeps bouncing between URLs.
curl -IL --max-redirs 10 https://problematic-site.com
Mixed Content Issues
Find redirects that accidentally downgrade from HTTPS to HTTP.
curl -IL https://example.com
Automated Redirect Chain Analyzer
Run a quick script to print each redirect and the status code.
#!/bin/bash
analyze_redirects() {
local url=$1
echo "=== Redirect Chain for $url ==="
curl -ILs -w "%{http_code} - %{url_effective}\n" "$url" | \
grep -E "HTTP|Location" | \
sed 's/HTTP\/[0-9.]* //'
}
analyze_redirects "https://example.com"
Detailed Header Analysis Script
Log all key headers to see where and how each redirect happens.
#!/bin/bash
detailed_trace() {
local url=$1
echo "=== Detailed Trace for $url ==="
curl -ILv "$url" 2>&1 | \
grep -E "^(<|>) (HTTP|GET|Host|Location|Server|X-)"
}
Combine Tools for Deep Diagnosis
Use DNS + redirect + timing tests to isolate where issues start.
dig example.com
curl -IL https://example.com
curl -w "Total time: %{time_total}s\n" -o /dev/null -s https://example.com
Test Multiple URLs at Once
Loop through several URLs to check their redirect behavior quickly.
urls=("https://site1.com" "https://site2.com" "https://site3.com")
for url in "${urls[@]}"; do
echo "Checking $url"
curl -ILs --max-redirs 3 "$url" | head -5
echo "---"
done
Monitor Redirect Changes Over Time
Compare redirects between runs to catch unexpected changes.
curl -IL https://example.com > redirect_snapshot.txt
curl -IL https://example.com | diff redirect_snapshot.txt -
Key Takeaways
- Use
-Ito fetch headers. - Use
-Lto follow redirects. - Add
-vfor detailed output. - Redirects can come from WordPress, server config, or CDN layers.
- Always check
Location,Server, andX-Powered-Byheaders to find the redirect source.
Conclusion
Redirects are an unavoidable part of website management—whether for SEO, site migrations, or protocol enforcement. However, because they can originate from anywhere in the stack—from a Cloudflare worker rule down to a line of JavaScript—they often turn into invisible performance killers or infinite loops.
The key to solving these issues is visibility. By moving away from the browser, which often masks the underlying mechanics with caching and auto-loading, and moving toward terminal tools like curl, you gain granular control over the diagnosis.
Remember that a redirect loop is rarely a single error; it is usually a conflict between two layers of your stack (e.g., your Nginx server forcing HTTPS while your WordPress database requests HTTP). By systematically using flags like -I to isolate headers and -L to trace chains, you can pinpoint exactly which layer is mishandling the request.