← All posts

Redirects in practice with 301, 302, and everything between


Redirects are one of those topics everyone thinks they understand until a migration goes sideways. The status codes are simple. The behavior of browsers, CDNs, and Googlebot layered on top of them is where the fun starts.

Here is how to use them in practice, distilled from the way migrations actually go right and wrong.

The codes, minus the folklore

A 301 says the move is permanent. A 302 says it is temporary. For SEO purposes, both pass signals. The old belief that 302s leak PageRank has been dead for years: Google has confirmed all 30x redirects pass PageRank, and a 302 left in place long enough gets treated as a 301 anyway.

That does not make the choice meaningless. A 301 tells Google to update the canonical URL immediately; a 302 tells it to keep the old URL indexed for now. Use 302 only when you genuinely intend to bring the old URL back: A/B tests, geo-holding pages, out-of-stock detours. Everything else is a 301.

Then there are 307 and 308, the method-preserving twins. A 307 is a temporary redirect that guarantees a POST stays a POST; 308 is its permanent sibling. For regular GET-based page moves they behave like 302 and 301 as far as search is concerned. The 307 you will meet most often is not from your server at all: Chrome shows an internal 307 when HSTS upgrades HTTP to HTTPS before the request leaves the browser.

Meta refresh and JavaScript as last resorts

Sometimes you do not control the server, and all you have is a meta refresh tag or window.location. Google processes both: an instant meta refresh (0 seconds) is treated roughly like a permanent redirect, and JS redirects get picked up at rendering time.

But they are strictly worse. They are slower for users, they depend on rendering rather than being processed at crawl time, and other search engines and LLM crawlers handle them inconsistently. Use them when the platform gives you nothing else, and label them as technical debt when you do.

Chains, loops, and the hop budget

Googlebot follows up to ten hops in a redirect chain; beyond that it gives up and reports a redirect error. But the practical budget is much smaller. Every hop adds latency for users, spends crawl budget, and multiplies the chance that one link in the chain is misconfigured.

Chains grow by accretion: HTTP to HTTPS, then non-www to www, then a trailing slash fix, then the page moves from the last two migrations. Five hops later, nobody remembers why. The working rule: every redirect should land on the final destination in one hop. After any migration, crawl your redirect sources and flatten anything with more than one step. Screaming Frog's redirect chains report does this in minutes.

Preserving signals during migrations

Redirects are how a migration keeps its accumulated equity. That only works when the mapping is right.

  1. Build a redirect map before anything moves: a spreadsheet of every legacy URL and its one-to-one destination. Crawl the live site, pull URLs from Search Console and analytics, and include the ones with backlinks even if they 404 today.

  2. Map to equivalents, not the homepage. Mass-redirecting everything to the root gets treated as a soft 404, and the equity evaporates.

  3. Keep the redirects live for the long haul. Google recommends at least a year; keeping migration redirects effectively forever is the safer habit, because backlinks do not expire.

  4. Verify with a fresh crawl in list mode over the old URLs the day of launch, then re-check Search Console coverage over the following weeks.

When a 410 is more honest

Not every dead URL deserves a redirect. If a page is gone with no meaningful equivalent, redirecting it somewhere vaguely related often gets classified as a soft 404 anyway. A 410 says: this existed, it is gone on purpose, stop asking. Google drops 410s from the index slightly faster than 404s, and either is a legitimate answer. Discontinued products with no successor, expired listings, deleted user content: let them go cleanly. Reserve redirects for URLs that have somewhere real to send people.

Audit it with curl

Browsers hide redirect behavior behind caches and extensions, so the command line is the place to verify. curl -I https://example.com/old-page shows the status and Location header for a single hop. Add -L to follow the whole chain and watch every response scroll past. curl -sILo /dev/null -w "%{num_redirects} hops to %{url_effective}\n" prints the hop count and final destination in one line, and a Googlebot user agent via -A catches sites that treat crawlers differently. If curl and your browser disagree, the CDN in between is usually making its own decisions.

Redirects are unglamorous plumbing. But in migration postmortems, the difference between a dip and a disaster is usually somebody maintaining a boring spreadsheet of URL mappings. Be that somebody.