Find and fix redirect chains and loops
A redirect is one of the easiest things on the web to get slightly wrong and never notice. Your browser follows the whole chain silently and shows you only the page at the end, so a site that bounces through four URLs looks identical to one that answers directly, apart from a delay you probably attribute to the network.
The cost is real. Every hop is a full round trip before any content starts to load. Chains also muddy which URL search engines should treat as the real one, and a misconfigured one can strand visitors in a loop. The first step to fixing any of it is to see the hops, which is what the Redirect and HSTS Tracer shows.
How chains build up
Almost nobody designs a four hop redirect. They accumulate, because each layer of a stack adds its own rule and none of them knows about the others.
A typical one starts with a visitor typing example.com:
http://example.com/redirects tohttps://example.com/(the HTTPS rule).https://example.com/redirects tohttps://www.example.com/(the www rule).https://www.example.com/redirects tohttps://www.example.com/en/(a language rule).https://www.example.com/en/redirects tohttps://www.example.com/en(a trailing slash rule).
Each rule is reasonable alone. Together they cost four round trips before the page begins. In a test of a three hop chain, the redirects alone took over two seconds before any content arrived, and the real figure depends on how far the visitor is from your server.
The fix is to make the first redirect go straight to the final destination.
The HTTPS upgrade should be one hop
The most important redirect is the one from plain HTTP to HTTPS, because it is the first thing a visitor who types a bare domain meets. It should reach the final, canonical address in a single step. In nginx, with www as the canonical host:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
# ssl_certificate lines here
return 301 https://www.example.com$request_uri;
}
An address on plain HTTP goes to the final https://www address in one hop, and the bare HTTPS domain goes there in one hop too. $request_uri keeps the path and query string, so deep links survive.
Use a permanent status for this. A 302 or 307 tells browsers and search engines the move is temporary, so the original URL stays the one to remember. The tracer flags a temporary redirect used for the HTTPS upgrade for that reason. 301 versus 302 redirects explains the difference, including where 307 and 308 fit.
One practical caution: browsers cache 301 responses aggressively. While you are still experimenting, use a temporary redirect, and switch to a permanent one once you are sure it is right.
Why loops happen
A loop is two or more rules that send a request back to each other. The browser gives up with "too many redirects". The common causes:
The CDN and the origin disagree about HTTPS. A CDN connects to your origin over plain HTTP, and your origin sees an HTTP request and redirects it to HTTPS. The CDN follows the redirect, connects over HTTP again, and the cycle repeats. The usual fix is to have the CDN connect to the origin over HTTPS, or to make the origin trust the forwarded protocol header from the CDN.
The apex and www rules point at each other. One layer, such as your DNS provider's redirect feature, sends example.com to www.example.com, while the application sends www back to the apex. Choose one canonical host and make every layer agree.
Two rules each add or remove something. One adds a trailing slash and the other removes it. One forces lowercase and another is case sensitive. These are easy to miss because each rule works alone.
A redirect that depends on a cookie or language. Redirecting based on a cookie breaks for clients that do not store cookies, and a language redirect can send a client to a URL that redirects it back.
The tracer stops at ten hops and reports when it sees a URL it has already visited, which marks a loop, so it shows the repeating part instead of timing out.
Beyond hops: what else the trace shows
Each hop in the tracer has more than a status code.
Timing. How long each hop took, so you can see which layer is slow.
HSTS. Whether HTTPS responses carry a Strict-Transport-Security header, and how long it lasts. HSTS only works on HTTPS responses, so the header on a plain HTTP hop is ignored, and the tracer only reports it for HTTPS. A lifetime shorter than 180 days is flagged, and one of a year or more with includeSubDomains and preload is marked as preload ready. See HSTS and preload explained before enabling preload, because it is hard to undo.
Cookies. The names, never the values, of cookies set on each hop. A cookie set on a plain HTTP hop travels unencrypted and can be read or changed in transit. The cookie flags guide covers Secure, HttpOnly and SameSite.
Downgrades. A hop from HTTPS back to HTTP exposes the visit and defeats HSTS. It is rare and usually a misconfigured redirect target, such as one with a hardcoded http://.
What it does not follow
The tracer follows HTTP redirects. It does not run JavaScript, so a redirect done with window.location is invisible to it. It does notice an HTML meta refresh on the final page and reports it, but it does not follow one. Both are weaker signals for search engines than a proper HTTP redirect, so prefer the real thing.
It also refuses addresses on private or local ranges, and it checks every hop against the same rule, so a redirect that points somewhere internal is stopped there.
A routine
Trace your bare domain with no scheme, which starts on plain HTTP as a visitor would. Count the hops and aim for one to reach the final URL. Check the status code on the HTTPS upgrade is permanent. Look for HSTS on the final response and for cookies set on plain HTTP hops. If anything loops, find which two layers disagree and make one the owner of the rule. After a change, trace again, and use the HTTP header checker for the full response of any single URL, and the security headers scanner to grade the headers on the final page.