← Back to Blog

The Complete Guide to HTTP Redirects: 301, 302, 307, 308 and SEO Pitfalls

A story about losing search rankings after a domain change

A site moved example.com entirely to example.net using the platform's default redirect. Three months later traffic halved—because the default was 302 (temporary), so search engines never transferred the old domain's weight to the new one, treating it as a temporary borrow. One line unlocks it: permanent change → 301, temporary borrow → 302, keep the method → 307/308. This article clarifies the four, and steers you past the SEO and form-redirect traps.

Four statuses in one table

Status Name Semantics Cached? Method change
301 Moved Permanently permanent ✅ (browser+SEO) POST→GET
302 Found temporary legacy POST→GET
303 See Other view with GET intentional to GET
307 Temporary Redirect temporary, keep method keep method
308 Permanent Redirect permanent, keep method keep method

Key: 301/308 are cached and permanent; 302/307 are not cached and temporary; 303 explicitly says "after redirect, please GET". The most-often-hit trap is 301/302 silently turning POST into GET.

The Location header and chains

A redirect tells the client where to go via a response header:

HTTP/1.1 301 Moved Permanently
Location: https://new.example.com/path/to/page
  • The client must read Location to decide the next hop;
  • a relative Location resolves to an absolute URL against the current one;
  • a link may chain through several hops (A→B→C→final); browsers follow to the end but give up and error past roughly 20 hops;
  • longer chains are worse for users and crawlers alike (extra round trips)—reach it in one hop where possible.

SEO trap: should weight transfer?

301 → passes as much link weight to the new URL as possible (do this for permanent migration)
302 → passes no weight, only borrows temporarily (A/B tests, launch transitions)
  • Substantially different content behind a 301 → read as a downgrade;
  • Whole-site 301-to-home → looks like dead-link aggregation; prefer per-path mapping;
  • For links you can't fully map, at least keep a maintained 301 table so the map doesn't go missing pages.

Redirect loops: what's going on

A→B→A, or A-self-reference, is a loop. Triggers usually live in middleware: trailing-slash rules, http↔https swaps, www canonicalization, or CDN-vs-origin rule conflicts. Defense and diagnosis:

# watch which hop returns to the origin
curl -I -L --max-redirs 10 https://old.example.com

Clients typically give up after ~20 hops; on the server, decide whether to redirect only after normalizing, to avoid ambiguous rules stepping on each other.

Self-check

Pick a status code for each need and justify it:

  1. Old pages all moving under archive/, old URLs retired permanently;
  2. Running a one-day gray release on a new design, possibly rolled back anytime;
  3. Redirecting a user back to a list page after a form submit.

(Answers: ①301; ②302 or 307; ③303 or 307/302 — explaining the equity/flow reasoning is what counts.) Being able to state the why means you've passed the semantics boundary test.

Frequently Asked Questions

What's the real difference between 301 and 302, and why can 302 turn POST into GET?

301 is a **permanent** redirect, 302 is **temporary**—the difference is *semantics*, not just behavior. **301 is cached** by browsers and search engines: later visits to the old URL go straight to the new one, and POST is rewritten to GET (migration is permanent, so duplicate-post risk surfaces under 301). **302 is not cached**, but legacy implementations also rewrite POST→GET, so a form/payment POST can turn into a GET that loses parameters. For 'temporary but keep the method' see 307/308. Rule of thumb: permanent migration → 301 (passes SEO weight); temporary jump → 302/307; temporary keeping method → 307; permanent keeping method → 308; to prevent POST→GET use 307/308.

Why does my redirect lose POST parameters?

Because the response is 302 or 301, and clients (browsers, default curl, old proxies) treat a redirect as 're-fire as GET', dropping the POST body and headers. To keep the method you have exactly two options: ①the server returns **307** (temporary, keep method) or **308** (permanent, keep method); ②use **303** to deliberately say 'after redirect, view with GET'—303 is an intentional conversion, not 'lost params'. If you need the original data later, don't rely on the browser replaying the POST; persist it in a server session and hand the target page a GET with query params/fragment.

Why do redirects loop forever, and how do I prevent it?

A loop happens when **Location points back to the same chain**: A → B → A, or a rewrite rule redirects A back to A. Common triggers: ①middleware that force-adds/removes trailing slashes while the path contains an oddity re-rewritten again; ②scheme/case rules (http↔https, www↔non-www) re-firing on an already-normalized URL; ③CDN origin and origin-server rules overriding each other. Prevention: add a **redirect hop guard** (clients/browsers error out after ~20 hops), and on the server decide whether to redirect only after normalizing. To debug, capture the Location chain and use `curl -L --max-redirs` to see whose jump returns to the origin.

Should old revamped links be 301 or something else?

**Use 301 whenever possible** — if the old URL is fully retired and the new one is semantically equivalent. 301 transfers most of the old page's search-engine **weight (link equity)** to the new URL, so rankings follow the new address; 302 does not transfer weight—it just borrows the page temporarily, keeping rankings on the old address (fine for a short-lived experiment). But note three preconditions: ①content is truly **equivalent** (301-ing substantially different content reads as a downgrade); ②map one-to-one rather than redirecting everything to the homepage (301-to-home rows look like dead-link aggregation to SEO); ③keep the 301 table/sitemap maintained so old links don't become orphans.

← Back to Blog