# "Avoid multiple page redirects": how to fix it

**Audit ID:** `redirects` · **Category:** Performance

<!--QA-->
> **Quick answer:** This audit fails when the requested URL redirects before serving the page, because each hop costs a full round trip before rendering starts. Fix it by collapsing the chain to a single hop, handling it at the CDN or server rather than in application code, and pointing internal links, sitemaps, and canonical tags at the final URL.
<!--/QA-->

Lighthouse fails this audit when the URL a visitor requests is not the URL that finally answers. Every hop in between is a full network round trip before a single byte of your page arrives. GTmetrix reports the same problem as **"Avoid landing page redirects"**, and Lighthouse's own description is **"Redirects introduce additional delays before the page can be loaded"**.

If you are here because a page shows **"too many redirects"** rather than because Lighthouse flagged a delay, that is a redirect *loop*, which is a different failure with different causes. Skip to [that section](#how-do-i-fix-too-many-redirects).

## TL;DR

- **What:** The requested URL redirects one or more times before serving the page.
- **Why it matters:** Each hop costs a round trip before rendering can start, and it is worst on mobile, where latency dominates.
- **Fix:** Collapse the chain to a single hop, fix it at the outermost layer (DNS or CDN, not the app), and point internal links at the final URL so most visitors never redirect at all.

## What does "avoid multiple page redirects" mean?

Lighthouse follows the navigation request for the main document and counts the hops to the final URL. The audit passes only at zero redirects, and the reported cost grows with each one.

The word "multiple" is misleading. A single redirect already fails the audit, and a single redirect is often fine in practice: redirecting `http://` to `https://` is correct and you should keep it. What you are hunting is chains, where several rules each fire in turn.

## How much does each redirect actually cost?

One hop is one round trip: the browser asks, the server says "not here, go there", and the browser starts again. On a fast desktop connection that is tens of milliseconds. On mobile, where round-trip latency is commonly 100 to 300 ms, a three-hop chain can cost most of a second before your HTML even begins downloading.

It gets worse when hops cross origins, because a new host means a fresh DNS lookup, TCP handshake, and TLS negotiation on top of the request itself. This is why link shorteners in ad campaigns are expensive: the shortener, then your marketing domain, then the canonical URL, each on a different host.

## How do I check a page's redirect chain?

The fastest check is one command. `-L` follows redirects, `-I` keeps it to headers:

```bash
curl -sSIL https://example.com | grep -iE '^(HTTP|location)'
```

Every `HTTP/…` line after the first is a hop you are paying for. For just the count and the destination:

```bash
curl -sS -o /dev/null -L -w 'hops: %{num_redirects}\nfinal: %{url_effective}\n' https://example.com
```

In DevTools, open the Network tab, tick **Preserve log**, and load the URL. The redirect responses stay visible instead of being replaced by the final page, and each one shows its status and `Location`.

Test the URL people actually type. `example.com` with no scheme, with and without `www`, and with and without a trailing slash will often reveal a longer chain than the canonical URL you have been pasting.

## What causes redirect chains?

Almost always independent rules stacking, each one reasonable alone:

- **Protocol then host.** `http://example.com` redirects to `https://example.com`, which redirects to `https://www.example.com`. Two hops, two separate rules, neither aware of the other.
- **Trailing slash normalization** running after a host rule, adding a third hop.
- **Case normalization**, where `/About` redirects to `/about`.
- **Locale or geo detection** sending `/` to `/en-us/`.
- **Legacy migrations** that were never collapsed: an old URL points at a newer old URL that points at the current one.
- **Link shorteners and tracking domains** in campaign links, which add hops before the request reaches you at all.
- **A retired mobile subdomain**, where `m.example.com` still redirects to the responsive site.

## How do I fix multiple page redirects?

1. **Collapse the chain to one hop.** Rewrite the rules so the first response goes straight to the final URL. `http://example.com` should land on `https://www.example.com` directly, not walk there. Most servers and CDNs let you match protocol and host in a single rule.
2. **Fix it at the outermost layer.** A redirect handled at the CDN edge never reaches your origin. A redirect handled in application code costs a full origin request first. Move the rule outward: DNS or CDN, then web server, then the app as a last resort.
3. **Point internal links at the final URL.** This is the fix people skip, and it is the one that helps most visitors: if your own navigation, sitemap, and canonical tags all use the final URL, most requests never redirect at all. Chase down absolute `http://` links in old content too.
4. **Remove the `http://` hop entirely with HSTS.** Send `Strict-Transport-Security` and browsers rewrite `http://` to `https://` internally on later visits, with no network request. Submitting to the HSTS preload list covers the first visit too. Start with a short `max-age` while you confirm every subdomain works over HTTPS, because the policy is hard to unwind once browsers have cached it.
5. **Re-check after each change.** Rules interact, and it is easy to remove one hop while adding another.

## Which redirect status code should I use?

- **301 Moved Permanently** for anything permanent, such as a URL change or host canonicalization. Search engines pass ranking signals through it and browsers cache it aggressively, which is exactly what you want, and also why a mistaken 301 is painful to reverse.
- **302 Found** for genuinely temporary moves, like a maintenance page.
- **308** and **307** are the strict versions of 301 and 302. They guarantee the method and body survive, so a POST stays a POST. Use them for API endpoints and form handlers, where a downgrade to GET silently loses data.

For a permanent host or protocol move, 301 is the right default.

## How do I fix "too many redirects"?

This is the error behind **"Safari cannot open the page because too many redirects occurred"**, Chrome's **`ERR_TOO_MANY_REDIRECTS`**, and the generic **"This page isn't working"** message. It is not a slow chain, it is a loop: two rules each sending the request to where the other one sends it back.

The causes, in the order worth checking:

- **A CDN SSL mode mismatch.** This is the most common single cause. If Cloudflare (or a similar proxy) is set to **Flexible** SSL, it talks to your origin over plain HTTP. Your origin sees an HTTP request and redirects to HTTPS. The browser is already on HTTPS, so it loops forever. Set the SSL mode to **Full (strict)** and the loop stops immediately.
- **Two host rules disagreeing.** One rule adds `www`, another strips it. Pick one canonical host and make sure only one rule expresses it.
- **Trailing-slash rules fighting.** The server adds a slash, the framework removes it.
- **The application and the server both redirecting.** A plugin enforces HTTPS while the web server does too, each seeing the other's output.
- **A login or session loop.** The app redirects an unauthenticated visitor to a login page that itself requires authentication, or a cookie the redirect depends on is never set because the domain or `Secure` flag is wrong. This is what "WordPress redirects to login page" usually turns out to be.

To see it, run the `curl -sSIL` command above. A loop shows as the same two `Location` values alternating. Reproduce in a private window, because a stale cookie or a cached 301 can keep a loop alive after you have fixed the cause. If you are a visitor seeing this on a site you do not run, clearing cookies for that site is the only fix available to you.

## How do I fix redirect problems in WordPress?

WordPress adds a few of its own, and they account for most of the WordPress-specific variants of this problem:

- **Site Address and WordPress Address disagreeing.** If one is set to `www` and the other is not, or one is `http` and the other `https`, every request loops. Check both under Settings, or set `WP_HOME` and `WP_SITEURL` in `wp-config.php` so they cannot drift.
- **Several plugins redirecting at once.** An SSL plugin, a redirect manager, and an SEO plugin can each add a rule. Deactivate them one at a time with `curl -sSIL` running between each to find the one that owns the hop.
- **`.htaccess` rules stacked from old migrations.** Rules accumulate and rarely get removed. Read the file top to bottom and collapse anything that chains.
- **A page redirecting to the homepage**, including the WooCommerce shop page, is usually a page-setting pointing at a deleted or draft page, or a permalink conflict, not a server rule. Re-save permalinks first, then re-check the page assignment.

## How do I verify the fix?

1. Re-run Lighthouse. "Avoid multiple page redirects" should move into the passed audits.
2. Confirm the chain is gone from every entry point, not just the canonical one:

   ```bash
   for u in example.com www.example.com http://example.com https://example.com/page/; do
     echo -n "$u -> "; curl -sS -o /dev/null -L -w '%{num_redirects} hops, %{url_effective}\n' "$u"
   done
   ```

3. Check your sitemap. Every URL in it should return 200 directly, with no redirect.
4. Spot-check internal links on a few key pages, since those are what most visitors follow.

## What mistakes should I avoid?

- **Removing the `http` to `https` redirect to pass the audit.** You would be breaking security to win a number. Use HSTS instead, which removes the round trip while keeping the guarantee.
- **Leaving the sitemap and canonical tags pointing at pre-redirect URLs.** You keep paying for hops on exactly the URLs you asked crawlers to visit.
- **Chaining instead of updating.** When a URL moves twice, point the oldest URL at the newest one, rather than at the middle step.
- **Using 302 for a permanent move.** Signals do not consolidate the way you expect, and the redirect is re-fetched rather than cached.
- **Debugging a loop in a normal browser window.** Cached 301s and stale cookies keep loops alive after the fix. Always confirm in a private window.

## Related audits

- [Document does not have a valid rel=canonical](/audits/canonical-tag), a canonical pointing at a redirecting URL wastes the same hops
- [Serve static assets with an efficient cache policy](/audits/efficient-cache-policy), the other cheap win that lives entirely in your response headers
- [Does not use HTTPS](/audits/use-https), where the protocol redirect this audit sees usually comes from

---

Audit your URL at https://lighthouse-md.com.
