A field guide to redirect status codes

A redirect is the server saying "not here — over there," with a status code that qualifies the statement. The qualifiers matter enormously, because browsers, proxies, and crawlers take them literally and act on them for a long time. The whole family sorts along two axes: is the move permanent? and must the request method be preserved?

301 — moved permanently

The strongest claim in the family: this resource now lives at the new address, forever. Clients are explicitly allowed to cache a 301 indefinitely — and many do. A browser that has seen a 301 once may never contact the original URL again; it rewrites the request locally and goes straight to the cached destination.

That's a feature when the move really is forever: search engines consolidate ranking signals onto the new URL, and everyone saves a round trip. It's a trap in every other case, because a cached 301 cannot be recalled. There's no header you can send later to take it back — you never get the request back in the first place.

302 — found (the messy one)

The original "temporary" redirect, with twenty-five years of baggage: historically, clients receiving a 302 on a POST would often retry the new location as a GET, changing the method mid-flight. The spec eventually shrugged and documented reality. For plain link-following in a browser it works fine; for APIs and forms it's ambiguous enough that the modern codes below exist specifically to replace it.

307 — temporary redirect, done right

307 makes two tight promises: the move is temporary, so ask again next time; and the method is preserved, so a POST stays a POST. "Ask again next time" is the operative clause for a link service — it means every hit comes back to the lookup table, so an edit to the destination is live on the very next request, for every client on earth, with no cache to chase down.

Pair it with cache-control: no-store and the behavior is fully deterministic: no client has any business remembering the answer.

308 — permanent, method-preserving

301's younger sibling: permanent like a 301, method-preserving like a 307. The right choice when an API endpoint moves forever. Shares the 301's defining property — and therefore its trap: cacheable until the heat death of the client.

the two-axis cheat sheet

codepermanent?method preserved?safe to edit later?
301yesno (historically)no — clients may never ask again
302noambiguousmostly, with legacy caveats
307noyesyes — rechecked every hit
308yesyesno — same cache trap as 301

why this service defaults to 307

Our one promise is that a link's destination stays editable for its whole life. A default of 301 would quietly break that promise one browser cache at a time: the link would seem editable, except for every visitor whose client had already memorized the old answer. 307 is the only code in the family where "change it later" works for everyone, always. So it's the default, and 301 is an explicit, per-link opt-in for moves you have genuinely sworn off ever revisiting — with this guide linked next to the field as a last warning.

Rule of thumb: reach for 301/308 only when you'd be comfortable carving the new URL into granite. For everything that might change — campaigns, docs, release notes, anything with a date in it — temporary codes are the honest ones.