Redirects

Choose a redirect that keeps the method you meant, and does not outlive its usefulness.

05of 18
≈ 11Min read
5Parts

Five codes, two questions

301 and 302 were specified to keep the method, and every browser did the opposite, so 303, 307 and 308 exist to say what you actually mean. If you are redirecting anything other than a plain page navigation, use one of those three.

CodePermanent?Keeps the method?For
301 Moved Permanentlyyes — cached hardno: POST becomes GETa URL that has moved for good
302 Foundnono: POST becomes GETa temporary detour (the vague one)
303 See Othernonever: always GETafter a form post — see below
307 Temporary Redirectnoyes, with the bodya temporary detour for an API
308 Permanent Redirectyesyes, with the bodya moved API endpoint

POST-redirect-GET, and the refresh that charges twice

Answer a successful form submission with a redirect to the result rather than with the page itself. The address bar then holds a URL that can be refreshed, bookmarked and shared — and the double-submit problem disappears without any JavaScript.

the reason 303 exists
POST /orders            → 201, and the browser shows the result
# the user presses refresh → the browser re-sends the POST → a second order

POST /orders            → 303 See Other, Location: /orders/482
GET  /orders/482        → 200
# refresh now re-sends the GET, which is harmless

301 is close to permanent

Search engines treat 301 as "move the ranking", which is why it is the right answer for a genuine move — and why it is worth getting right first time.

Browsers cache a 301 aggressively, sometimes forever

Ship a 301 to the wrong place and visitors keep going there after you fix it — their browser never asks again. Use 302 or 307 while you are unsure, and add Cache-Control: max-age=3600 to a 301 so there is a way back.

Chains, loops and the cost

Collapse chains into one hop wherever you can: redirect straight to the final URL. Browsers stop after about twenty hops and show an error; a loop between two rules is the classic cause, usually one in the CDN and one in the application, each unaware of the other.

each hop is a full round trip
http://doors.test        → 301 → https://doors.test
https://doors.test       → 301 → https://www.doors.test
https://www.doors.test   → 302 → https://www.doors.test/en
# three round trips before a single byte of HTML
HSTS removes the first hop

A Strict-Transport-Security header tells the browser to go straight to https next time, without asking http first. That is one round trip saved on every first visit, and one fewer moment where a plain-text request exists at all.

In code

and what fetch does about it
// Express
res.redirect(308, "/api/v2/orders");

// Fetch follows redirects by default and hides them
const res = await fetch("/old");
res.redirected   // true
res.url          // the final URL

// or handle them yourself
await fetch("/old", { redirect: "manual" });   // opaque response, status 0
await fetch("/old", { redirect: "error" });    // reject instead

Practice