Overview

A redirect tells the browser and the crawler that one URL has moved to another. The status code controls how the redirect is cached, whether ranking signals transfer, and how the request method is preserved. The wrong status code or a sloppy redirect map can split rankings across the old and new URLs, lose link equity to redirect chains, or produce soft 404s that get dropped from the index. This page covers the status codes, the redirect-map hygiene that keeps a migration clean, and the failure modes that account for most lost traffic.

Use 301 for permanent moves; that is the default

301 (Moved Permanently) is the default. It tells Google the new URL replaces the old one, transfers ranking signals fully, and gets cached aggressively by browsers and crawlers.

  • Permanent URL changes: slug rewrites, folder restructures, domain migrations.
  • Consolidating duplicate URLs into one canonical.
  • HTTPS migration: every http:// URL 301s to its https:// counterpart.
  • Trailing-slash policy enforcement: pick one form, 301 the other.
# Nginx: 301 redirect example
location = /old-article {
  return 301 /new-article;
}

The 301 takes effect for Google on the next crawl, but signal consolidation can take weeks. Do not redirect a 301 again two months later; that creates a chain and erodes the signal.

Use 302, 307, and 308 only when you mean them

The non-301 redirects have specific purposes; using them by accident is a common bug.

  • 302 (Found): temporary redirect. Tells Google to keep indexing the original URL. Use for A/B tests, geo-redirects, maintenance mode. Rarely the right answer; if the move is permanent, use 301.
  • 307 (Temporary Redirect): like 302 but preserves the HTTP method. Used in HSTS upgrades from http:// to https://. Almost never written by hand.
  • 308 (Permanent Redirect): like 301 but preserves the HTTP method. Use when POST and PUT requests must redirect without becoming GET. Rare on content sites; common on APIs.

The default reading of 302 by crawlers used to be “keep the old URL indexed indefinitely.” Google now treats long-lived 302s as 301s, but the lag costs weeks. Pick 301 for permanent moves; pick 302 only when temporary is true.

See http-status-codes for the full status code reference.

Keep redirect chains to fewer than three hops

Each hop in a redirect chain costs latency, drops a small amount of ranking signal, and increases the chance Google stops following before reaching the final URL.

  • 1 hop: ideal. /old 301 to /new.
  • 2 hops: tolerable during a migration window; flatten within a sprint.
  • 3+ hops: ranking loss accumulates; Googlebot may stop following.
  • Loops: any chain that returns to a URL already seen. Crawlers drop the URL; users see a browser error.

The fix on every migration is the same: rewrite the redirect map so every old URL points directly at the current final URL, not at an intermediate.

# Audit chains with curl
curl -sIL https://example.com/old-url | grep -E "^(HTTP|Location)"
# Each HTTP line is a hop. More than three is a bug.

Decide between redirect and canonical by intent

301 and rel="canonical" look similar but behave differently. Pick by what the user should see.

  • 301: the user should never see the old URL again. Browser shows the new URL in the address bar. Use for permanent moves and consolidation.
  • Canonical: both URLs should remain accessible; one is preferred for ranking. Browser stays on the URL the user visited. Use for parameter variants, print versions, AMP pairs.

A 301 to a noindex page does the opposite of what you want; the destination drops from the index and takes the source with it. Always 301 to an indexable destination. See technical for the canonical mechanics.

Preserve query strings or strip them cleanly

Decide per redirect whether query strings carry forward.

  • Tracking parameters (utm_*, fbclid, gclid): strip them on the redirect. The destination should not be indexed under multiple parameter combinations.
  • Functional parameters (?page=2, ?lang=fr): preserve them. Stripping breaks the page intent.
  • Search query parameters (?q=foo): preserve them when the destination is the same search page; strip when the destination is a static landing page.
# Nginx: 301 and strip all query strings
location = /old-page {
  return 301 /new-page;
}
 
# Nginx: 301 and preserve query strings
location = /old-page {
  return 301 /new-page$is_args$args;
}

The default in most servers is to drop query strings on redirect. Override only where the parameter is load-bearing.

Maintain a redirect map during migrations

Every URL change goes through a redirect map. The map is a CSV with old URL, new URL, status code, and a date. Treat it as a permanent artifact, not a deploy-time script.

  • Source-control the map. Every migration produces a new version; never overwrite history.
  • One entry per URL. If a URL has been moved three times, only the original-to-current entry stays in the active map; the intermediate entries get retired.
  • Test the map before deploy: every old URL returns 301, every destination returns 200, no chains, no loops.
  • Run a 404 audit one week after launch: pull GSC’s “Not found (404)” report and add map entries for any high-traffic 404s.

See seo-migration-playbook for the end-to-end migration sequence and site-architecture for how the URL structure decisions feed the map.

Common errors

  • Chain depth. The most common migration bug; old URL A 301s to B, which a previous migration 301s to C. Flatten so A 301s straight to C.
  • Redirect loop. A 301 to B, B 301 back to A. Crawler stops; user gets a browser error. Run the curl audit before deploy.
  • Redirect to a noindex page. Destination is excluded; source is replaced by the noindex destination and drops out of the index too.
  • Redirect to a 404. The redirect resolves to a missing page; the URL disappears from the index. Validate every destination returns 200 before adding to the map.
  • Soft 404: a URL returns 200 OK but the page is empty or a “this page does not exist” template. Google classifies as 404 and drops it. Return real 404 status for missing pages, or 301 to the closest equivalent.
  • Meta-refresh and JS redirects as a substitute for 301. Both work for users; both transfer ranking signal poorly. Use server-side 301 unless the server cannot issue redirects.
  • 302 used for a permanent move. Google eventually treats it as 301, but the delay costs weeks of ranking consolidation. Use 301 from the start.
  • Redirecting the entire old site to the homepage. Every URL becomes a soft 404 at scale and the new homepage absorbs no signal from the old deep pages. Map old URLs to the closest new equivalent; only redirect to the homepage as a last resort.