Redirect chains and loops
What is this issue?
A redirect chain is a redirect that takes more than one hop to reach the page that answers:
/old-pricing → 301 → /pricing → 301 → /pricing/ → 200 OKEvery hop is another request and response before the page starts loading. Chains usually build up over time: a page moves, then moves again, and nobody updates the first redirect.
For a page to pass this check:
- Every redirect reaches its destination in a single hop.
Example: http://example.com/blog redirects to https://example.com/blog, which
redirects to https://www.example.com/blog, which redirects to
https://www.example.com/blog/. Three hops where one would do.
Loops are a separate check. A redirect that comes back to an address already in its chain never arrives at all. That is reported as "Redirect loop" (#166), and a URL that loops is reported there only — not here as well. This check's title still reads "Redirect chains and loops" because check titles are kept stable; it reports chains.
Why it matters
Every hop costs time. Each extra redirect is a round trip — sometimes a new connection to a new host — before the reader sees anything. On mobile networks a three-hop chain can add most of a second.
Crawlers spend budget on hops. Each hop is a request a crawler makes instead of fetching a real page. Google follows up to ten hops and then gives up on the URL.
Long chains are fragile. Every hop is a rule that has to keep working. Remove one in a later clean-up and every link into the start of the chain breaks.
Link signals travel best in one hop. Links pointing at the start of a chain reach the destination only through every redirect along the way.
Effect on the health score
This is an important issue under the Link Integrity lens.
How to fix it
Read the chain on the finding. It lists every hop from the first address to the page that answers.
Point the first redirect straight at the final address.
# Before: two hops location = /old-pricing { return 301 /pricing; } location = /pricing { return 301 /pricing/; } # After: one hop from each old address location = /old-pricing { return 301 /pricing/; } location = /pricing { return 301 /pricing/; }Combine host and protocol redirects. Redirect
http://example.com,http://www.example.comandhttps://example.comstraight to the one canonical origin in a single rule, rather thanhttp → httpsand thennon-www → www.Update internal links to the final address, so readers on your own site never take the chain at all (see "Internal links pointing at a redirect", #139).
Re-scan to confirm each address now takes one hop.
Examples
Example 1: A page that moved twice
Fails (2 hops):
/old-pricing → 301 → /pricing → 301 → /pricing/ → 200 OKPasses (1 hop):
/old-pricing → 301 → /pricing/ → 200 OKExample 2: Protocol and host fixed in separate steps
Fails (2 hops):
http://example.com/blog → 301 → https://example.com/blog → 301 → https://www.example.com/blog → 200 OKPasses (1 hop):
http://example.com/blog → 301 → https://www.example.com/blog → 200 OKExample 3: A loop — reported elsewhere
/a → 301 → /b → 301 → /a → …Not reported here. This never arrives; it is a redirect loop (#166).
Example 4: A single redirect
/old → 301 → /new → 200 OKPasses. One hop is how a moved page should behave.
How PixyScan detects this
Follows redirects during the crawl. When a page the crawler requests redirects, PixyScan records every hop (address and status code), the final address, and whether any address repeated.
Counts the hops after the crawl. A recorded chain of more than one hop is reported against the address that started it, with the full chain.
One redirect is fine. A single
301from an old address to a new one is the normal, correct way to move a page. If its status code is wrong (for example a302for a permanent move), that is "Redirects using the wrong status code" (#7).Loops are reported elsewhere. A chain that repeats an address is a redirect loop and is reported only as #166, never also here.
Respects your lens settings. Nothing is raised on a scan with the Link Integrity lens switched off.
What we store
Storage Level
Page Level
Database Table / Prisma Model
RedirectChain (redirect_chains). The finding is stored on
audit_issues.details.
Fields Used
| Field | Type | Description |
|---|---|---|
| redirect_chains.original_url | String | The address the crawler requested |
| redirect_chains.final_url | String | The address that answered |
| redirect_chains.chain | Json | Every hop, [{ url, statusCode }] |
| redirect_chains.chain_length | Int | Number of hops. More than 1 raises this check |
| redirect_chains.has_loop | Boolean | True for a loop, which is #166 instead |
Stored Fields on the finding
| Field | Type | Description |
|---|---|---|
| message | String | "Redirect chain detected (2 hops)." |
| originalUrl | String | Where the chain starts |
| finalUrl | String | Where it ends |
| chainLength | Int | Number of hops |
| hasLoop | Boolean | Always false here |
| chain | Json | The hops |
Detection Dependencies
- HTTP Response — the redirect hops the crawler followed