Views: 26

307 vs 308 Redirect: Method-Preserving Redirects

307 vs 308 redirects keep the request method — unlike 301/302, which turn POST into GET. Check which code your server returns. Free instant check.

Check your domain for this issue now

Free, no sign-up. Runs the exact check this guide describes and shows what to fix.

Problem

You need to send a redirect but the request is a POST, PUT, or PATCH — an API call, a form submission, a webhook — and you have to pick between 301, 302, 307, and 308. Get it wrong and the request body disappears.

Symptoms

  • A POST to an old URL arrives at the new URL as a GET with an empty body, and the server rejects it or silently does nothing.
  • HTTP Check shows a 301 or 302 hop in a chain that should have preserved the method.
  • A form submission “works” in the browser but the same call from curl, a mobile app, or a server-to-server client loses its payload.
  • Chrome shows a 307 for an http→https redirect you never configured (that one is HSTS — see below).

What 307 and 308 Actually Change

There are two independent questions hiding inside “which redirect code,” and mixing them up is the whole problem.

Question one: permanent or temporary? That is the caching and SEO axis. 301 (RFC 9110 §15.4.2) and 308 (RFC 7538, folded into RFC 9110 §15.4.9) are permanent — cacheable by default, and search engines consolidate ranking signals onto the target. 302 (§15.4.3) and 307 (§15.4.8) are temporary — not cached unless you explicitly say so, and treated as “this is a detour, keep indexing the original.”

Question two: does the method survive? This is where 307/308 earn their existence. When a browser follows a 301 or 302 for a POST, almost every client rewrites the method to GET and throws the body away. That behavior is a 20-year-old wart: the spec always said the method should be preserved, but early browsers changed it, so much of the web quietly depends on POST→GET rewriting that 303 See Other later made official. 307 and 308 close that door. They require the client to repeat the exact same method and body against the new URL. A POST stays a POST.

So the four codes are a 2×2 grid, not a list:

  • 301 — permanent, method may change (POST becomes GET). Fine for GET page moves.
  • 302 — temporary, method may change. Fine for temporary GET detours.
  • 307 — temporary, method preserved. Use for a temporary redirect of a POST/PUT/PATCH.
  • 308 — permanent, method preserved. Use for a permanent redirect of an API or form endpoint.

For SEO the grid collapses back to two: Google treats 308 like 301 and 307 like 302. The method axis is the only reason to reach past 301/302.

The HSTS 307 That Isn’t Yours

If you are staring at a mysterious 307 on an http://https:// redirect you never wrote, stop debugging your server. Once a domain has sent a Strict-Transport-Security header, the browser upgrades every future http:// request to https:// internally, before any packet leaves the machine. Chrome’s DevTools renders that as a 307 internal redirect with Non-Authoritative-Reason: HttpsUpgrade. It is not on the wire, Googlebot never sees it (crawlers observe the server’s real 301), and no config change removes it — it is cached in the browser’s HSTS store. This is the single most common “phantom 307” and it wastes hours because people go looking for a rule that doesn’t exist.

Top 3 Causes of the Wrong Code

  1. A framework default rewrote your POST - Many web frameworks and reverse proxies emit 301 or 302 for redirect() calls. If the redirected request was a POST, the client re-issued it as a bodyless GET and your handler saw nothing. Switching those specific redirects to 307/308 is the fix.
  2. Trailing-slash or http→https normalization on an API - Normalizing /api/thing to /api/thing/ (or bumping to HTTPS) with a 301/302 breaks every non-GET call to that endpoint. API and webhook redirects must be 307/308, or better, avoid redirecting writes at all.
  3. A “temporary” 302 that should have been permanent (or vice versa) - A permanent move served as 302 keeps search engines indexing the old URL; a temporary detour served as 301/308 gets cached hard by browsers and is painful to walk back. Match the permanence to reality.

Diagnose with DechoNet

  • HTTP Check to read the exact status code and the full hop chain for a URL. Confirm whether each hop is 301/302/307/308 and count the hops — a redirect that should preserve a method but shows 301/302 is your culprit.
  • Reproduce the write path deliberately: the browser hides the POST→GET rewrite because navigation is usually GET anyway. The failure shows up when a curl -X POST, an SDK, or a server-to-server client follows the redirect. Test with the same method the real caller uses.

Resolution Checklist

  • For any redirect that can carry a request body (POST/PUT/PATCH — APIs, forms, webhooks), use 307 if temporary or 308 if permanent.
  • For plain GET page moves, 301 (permanent) or 302 (temporary) is fine — the method stays GET regardless.
  • Match permanence to reality: permanent moves get 301/308, genuine temporaries get 302/307. Remember 301/308 are cached hard by browsers and hard to undo.
  • Prefer not redirecting writes at all. If you can, have clients target the final URL directly instead of relying on method-preserving redirects.
  • If you see an unconfigured 307 on http→https, confirm it is the HSTS internal redirect (Non-Authoritative-Reason: HttpsUpgrade) and stop editing server config.
  • Re-run HTTP Check and confirm both the code and the hop count match your intent.

When to Escalate

  • If a redirect is added by a CDN or reverse proxy above your application, the code may be set at the edge, not in your app — check the CDN’s redirect and HTTPS/trailing-slash normalization rules, since those often default to 301/302 and will strip POST bodies before your origin ever sees them.

Related Tools

Related Guides

Share this guide

[Ad] Guide Detail Inline
← Back to All Guides