301, 302, 307 or 308? Permanent vs temporary redirects, and why POST turns into GET
The differences between HTTP redirects 301, 302, 303, 307 and 308, from RFC 9110 and the WHATWG Fetch standard: when POST becomes GET, and how caches and search engines treat each code.
If a form was submitted with POST but the server log shows only a GET to the new address, with an empty body, check the status code of the redirect in between. A client that receives 301 or 302 is allowed to repeat the request as GET instead of POST. The Fetch algorithm used for browser requests and curl’s default -L behavior make that conversion for POST. With 307 and 308 the method stays the same.
Two distinctions separate the four codes
RFC 9110, the HTTP specification, groups 301, 302, 307 and 308 as redirects that say “this resource might be available at a different URI, given in the Location header.” Two questions tell them apart:
- Is the move permanent or temporary?
- When the redirect is followed automatically, may the request method change?
| POST→GET allowed | Method kept | |
|---|---|---|
| Permanent | 301 Moved Permanently | 308 Permanent Redirect |
| Temporary | 302 Found | 307 Temporary Redirect |
Figure: a simplified choice matrix for picking a code. Differences in caching, search engines and client implementations, and the exceptions, are covered in the text.
301 and 308 mean the resource has been given a new permanent URI and future references ought to use it. 302 and 307 mean the resource is only temporarily somewhere else, so the client ought to keep using the original URI for future requests.
On the method, 307 is the most explicit: a user agent that follows the redirect automatically MUST NOT change the request method. RFC 9110’s section on 308 has no such sentence. Instead, the specification says 307 and 308 were added “to unambiguously indicate method-preserving redirects,” and the 301 section points to 308 for cases where changing the method is undesired.
Why 301 and 302 turn POST into GET
The 301 and 302 sections of RFC 9110 each carry a note: “for historical reasons,” a user agent MAY change the request method from POST to GET for the subsequent request.
The specification gives the background. In HTTP/1.0, 301 and 302 were defined as method-preserving. Early user agents split on whether to resend a POST as POST or switch to GET, the safer choice when redirected to a different site, and prevailing practice converged on GET. 307 and 308 were added later to mark method-preserving redirects unambiguously, and the definitions of 301 and 302 were adjusted to allow a POST to be redirected as GET.
So a method change on 301 or 302 is not a spec violation. It is an option the spec allows. Keeping the method is allowed too, and the client implementation decides which one happens.
303 means “get the result somewhere else”
303 See Other works differently from the four codes above. It doesn’t say the original resource has moved. It says an indirect response to the request is available at another resource, which the user agent retrieves with GET or HEAD. RFC 9110 describes its main use as sending the output of a POST to a separate resource, so that the result can be identified, bookmarked and cached on its own. If the intent after handling a form is to open a result page with GET, 303 states that intent more precisely than 302.
What browsers do: the Fetch standard
How browsers follow redirects is set out in the “HTTP-redirect fetch” steps of the WHATWG Fetch standard. It defines 301, 302, 303, 307 and 308 as redirect statuses, and it changes the request method to GET and drops the body when:
- the status is 301 or 302 and the method is POST, or
- the status is 303 and the method is neither GET nor HEAD.
307 and 308 aren’t in these method-conversion conditions. When the redirect can be followed, Fetch therefore does not replace the method and body with GET and a null body. The conditions also show that this conversion does not apply to PUT or DELETE on 301 or 302.
That does not mean every request body can always be replayed. Earlier in the same procedure, Fetch returns a network error for a non-303 redirect when the request body cannot be replayed.
The same steps set other rules. Once the redirect count reaches 20, the fetch ends in a network error. When the request moves to a different origin, the Authorization header is removed. RFC 9110 also says a client SHOULD detect and intervene in cyclical redirects, and notes that an earlier specification (RFC 2068) recommended a maximum of five, so some clients may still have a fixed limit like that.
Checking with curl
curl’s -L (--location) behaves the same way. According to the curl manual, when a POST gets a 301, 302 or 303 response, curl sends the next request as GET. For any other 3xx code it keeps the method. The --post301 and --post302 options keep POST on 301 and 302.
I ran a local check. A small Python server redirected /from/<code> to /to with that status code, and /to echoed the method and body it received. The client was curl 8.7.1 on macOS.
for c in 301 302 303 307 308; do
printf "$c -> "; curl -s -L -d 'x=1' http://127.0.0.1:8765/from/$c
done
printf "301 --post301 -> "; curl -s -L --post301 -d 'x=1' http://127.0.0.1:8765/from/301
Actual output:
301 -> GET /to body=b''
302 -> GET /to body=b''
303 -> GET /to body=b''
307 -> POST /to body=b'x=1'
308 -> POST /to body=b'x=1'
301 --post301 -> POST /to body=b'x=1'
This output comes from one run in that environment (macOS, curl 8.7.1), and the test server’s code isn’t included here. Other curl versions or other HTTP clients may behave differently, so check in the environment you actually use.
Caches and search engines
The permanent/temporary split also matters for caching. RFC 9110 classes 301 and 308 as “heuristically cacheable”: even without explicit expiry information, a cache may estimate a freshness lifetime and reuse the response. 302 and 307 are not in that class. Either way, explicit cache controls such as Cache-Control take precedence. So if a 301 is used for a move that might be undone, clients that already cached it may keep reusing the old redirect after it’s reverted. If a revert is possible, use a temporary code or set cache controls explicitly.
Search engines follow their own policies, not the HTTP specification. According to Google Search Central (last updated 14 April 2026), Google treats 301 and 308 as permanent redirects and uses them as a signal that the target URL should be canonical. It treats 302, 303 and 307 as temporary and doesn’t use them as that signal, though the target page may still be indexed if other canonicalization signals point to it.
Which one to use
- For a permanent move, such as a new domain or several addresses folded into one, use 301 or 308. If you moved an API endpoint that receives POST, 308 keeps the method.
- For a temporary detour, such as sending users to a notice while a service is down, use 302 or 307. Use 307 if the method and body must be kept.
- To have the browser open a result page with GET after a form is handled, use 303.
One more thing: 308 was first defined in an experimental RFC in June 2014, much later than the other codes, and RFC 9110 notes that for this reason it might not be recognized everywhere. If you have to support older clients, test on those clients.
Specifications and documentation used
- RFC 9110 — HTTP Semantics (2022, STD 97): the redirect status codes (301, 302, 303, 307, 308) and the historical notes in section 15.4, and the heuristically cacheable status codes in section 15.1. This is the HTTP rule the article refers to.
- RFC 7538 — The Hypertext Transfer Protocol Status Code 308 (Permanent Redirect) (2015): the earlier definition of 308, now obsoleted by RFC 9110.
- WHATWG Fetch Standard — HTTP-redirect fetch (Living Standard): for browser request processing, the redirect statuses, the method-conversion conditions, the redirect limit and header handling when the origin changes.
- curl man page: the
-L,--post301and--post302options. Documentation for one tool, which can change between versions. - Google Search Central — Redirects and Google Search (last updated 14 April 2026): how Google Search treats each redirect. One search engine’s policy.
Links and content were checked on 29 September 2026. Fetch is a living standard, and the Google page may change too.