| Permanent |
|---|
| 301 | Moved Permanently | Method not guaranteed | The URL moved for good — domain migration, trailing-slash normalization, http to https. Search engines merge ranking signals into the target. |
| 308 | Permanent Redirect | Method and body | A permanently relocated endpoint that receives POST, PUT, or PATCH calls — the request replays verbatim at the new URL, forever. |
| Temporary |
|---|
| 302 | Found | Method not guaranteed | The default temporary move — maintenance pages, A/B routes, short-term rebrands. Browsers re-check it instead of caching it. |
| 303 | See Other | Always GET on follow-up | After a POST form submit — redirect to a result page so refresh and the back button never re-submit the form (the Post/Redirect/Get pattern). |
| 307 | Temporary Redirect | Method and body | A temporarily moved endpoint that must still receive the original POST or PUT body intact — for example re-authentication flows that replay the request after login. |
| Method preservation |
|---|
| 301 + POST | Historic rewrite | POST may become GET | Pre-RFC 7231 browsers rewrote POST to GET on 301, and many still do for compatibility. Never point a 301 at an endpoint that expects a body. |
| 302 + POST | Ambiguous by design | GET in practice | RFC 1945 left 302 undefined for POST, and de-facto browser behavior became rewrite-to-GET. Codes 303 and 307 exist to close exactly this gap. |
| 303 + POST | Guaranteed GET | GET or HEAD only | The only code that mandates the follow-up method. Use it when the client must never replay a body — payment callbacks, form result pages. |
| 307 + POST | Never rewritten | Re-sends body verbatim | The client re-issues the identical method, headers, and body to the Location URL. The target must be ready to accept the full request again. |
| 308 + POST | Never rewritten | Re-sends body verbatim | The same guarantee as 307 with permanent semantics — safe for relocated API endpoints and webhook receivers. |
| Authorization | Dropped cross-origin | Credentials stripped | fetch and browsers remove the Authorization header when a redirect crosses origins. Serve the hop same-origin, or re-authenticate at the target. |
| Large bodies | Re-upload cost | Full re-transmission | A 307 or 308 on a PUT upload makes the client send the entire body twice. Prefer a direct signed URL over a redirect for large transfers. |
| Caching & SEO |
|---|
| 301 cache | Sticky by default | Cached indefinitely | Browsers remember a 301 across restarts with no built-in expiry. Ship one only for moves that are truly forever — undoing it takes a cache purge. |
| 302/307 cache | Re-checked each load | Not cached by default | Temporary codes are revalidated on every navigation unless Cache-Control extends them. Safe for experiments and staged rollouts. |
| 308 cache | Sticky like 301 | Cached indefinitely | Treat 308 with the same permanence caution as 301 — browsers keep following it long after you change your mind. |
| HSTS | Internal upgrade | No network redirect | Once Strict-Transport-Security is set, the browser rewrites http to https internally before any request leaves — your server-side 301 to https stops being observed. |
| upgrade-insecure-requests | CSP-driven upgrade | Internal, one-way | This CSP directive upgrades subresource http links to https with an internal redirect. Add it to retire mixed-content warnings without touching markup. |
| rel=canonical | Align with the redirect | Indexation signal | Point the canonical tag at the same final URL the redirect lands on. A canonical that disagrees with Location confuses crawlers and splits signals. |
| Chains | One hop only | Latency per hop | Each extra hop adds a round-trip and dilutes link equity. Flatten old to middle to new chains into a single old to new redirect. |
| Search engines | Signal handling | 301 merges, 302 defers | Google treats a 301 as a permanent move and consolidates ranking into the target; a 302 keeps the origin indexed, which is correct for geo or device variants. |
| Cache-Control | Explicit redirect caching | You set the TTL | CDNs and proxies may cache redirect responses. Set Cache-Control max-age explicitly on the 3xx so intermediaries hold it only as long as you intend. |
| Location | Required target | Absolute or relative | Every 3xx needs a syntactically valid Location URI; relative values resolve against the original request URL. Keep it final — never point it at another redirect. |