Flattening Multi-Hop Redirect Chains Into Single Hops
Problem Statement
After several migrations, a single old URL can pass through two, three, or more redirects before reaching its final destination — each hop adding latency and burning crawl budget. You need to detect every chain and rewrite each starting URL so it points directly at the final 200 target in one 301. This page sits under Redirect Chain Elimination.
When to Use This Approach
- A crawl or
curl -ILshows more than one redirect before a 200. - The site has been migrated more than once, layering redirect rules over time.
- Googlebot reports “Redirect error” or “Page with redirect” on URLs that should resolve cleanly.
- Latency on entry URLs is inflated by sequential hops, hurting Time to First Byte.
- A framework redirect and an old server rule both fire on the same path.
Step-by-Step Instructions
1. Detect the Chain with curl
curl -IL follows redirects and prints every hop. Count the HTTP status lines — anything more than one redirect before the final 200 is a chain to flatten.
# Follow all hops and show only status and Location for each step
curl -sIL https://www.example.com/old-page | grep -iE '^HTTP|^location'
2. Extract the Final Destination for Each URL
For every entry URL in your list, capture the last URL curl actually lands on. That final URL becomes the single destination for the flattened rule.
# Print "start_url final_url" for each line in urls.txt
while read u; do
printf '%s %s\n' "$u" "$(curl -sIL -o /dev/null -w '%{url_effective}' "$u")"
done < urls.txt > flattened-map.txt
3. Build a Rewrite Map Pointing Start Directly at Final
Convert flattened-map.txt into server rules where each original URL 301s straight to the final target — skipping every intermediate hop.
# Nginx map: each original path goes directly to the final destination
map $request_uri $flat_target {
/old-page /final-page;
/interim-page /final-page;
}
server {
if ($flat_target) { return 301 $flat_target; }
}
# Apache equivalent — original and any interim path both jump to the final target
RewriteRule ^/old-page/?$ /final-page [R=301,L]
RewriteRule ^/interim-page/?$ /final-page [R=301,L]
4. Remove the Now-Redundant Interim Rules
Once both the original and interim paths point at the final target, delete the old chained rules so a future request cannot re-enter the chain through a stale entry point.
# Confirm no rule still points to an interim hop before deleting it
grep -n 'interim-page' /etc/nginx/conf.d/redirects.conf
Worked Example
Before flattening, /old-page chains through two hops:
$ curl -sIL https://www.example.com/old-page | grep -iE '^HTTP|^location'
HTTP/2 301
location: https://www.example.com/interim-page
HTTP/2 301
location: https://www.example.com/final-page
HTTP/2 200
After adding /old-page → /final-page and /interim-page → /final-page to the map:
$ curl -sIL https://www.example.com/old-page | grep -iE '^HTTP|^location'
HTTP/2 301
location: https://www.example.com/final-page
HTTP/2 200
One hop instead of two, with the interim URL also resolving directly so no entry point re-enters the chain.
Verification
- Re-run
curl -sILon every flattened URL and confirm exactly one 301 before the 200. - Batch-check the whole list: count status lines per URL and flag any with more than two
HTTPlines. - After deploy, watch Google Search Console for “Page with redirect” counts dropping over subsequent crawls.
FAQ
How many hops are acceptable? One. Search engines tolerate several hops, but every extra redirect adds latency and wastes crawl budget. Flatten every chain so each entry URL reaches its final destination in a single 301.
Why redirect the interim URL too? Because the interim URL is often still linked or indexed. If you only fix the original, a request that enters at the interim path still resolves in one hop — but leaving the interim chained would let it re-form a multi-hop path.
Related
← Back to Redirect Chain Elimination