How HTTP Works

Comparison

301 vs 308: Permanent Redirects and POST Requests

301 vs 308: both signal a permanent move, but only 308 preserves POST. Compare browser handling, Google indexing, cache controls, and curl traces.

Bottom line: Either works for a permanent GET URL move. Use 308 when the redirected request must keep its method and body.

By How HTTP WorksReview process
301 Moved Permanently
vs
308 Permanent Redirect

TL;DR: For a permanent move of a GET page, either 301 or 308 works. Use 308 when a redirected POST must arrive as POST with its body intact. Google treats both as permanent redirects; method preservation is the deciding difference.

Side By Side

Behavior301 Moved Permanently308 Permanent Redirect
MeaningResource has a new permanent URLResource has a new permanent URL
Redirected GETRemains GETRemains GET
Redirected POST in browser FetchBecomes GET; body discardedMethod and replayable body preserved
HTTP method rulePOST-to-GET conversion is permittedMethod must be preserved when following automatically
Cache eligibilityHeuristically cacheable, subject to method and cache controlsSame
Google SearchPermanent redirect; destination is a canonicalization signalSame documented treatment

The status semantics come from RFC 9110 §15.4.2 and §15.4.9. Browser method handling follows the Fetch redirect algorithm. The table assumes the client follows the redirect and can replay the body: Fetch can fail a redirect when the request body has no replayable source.

When To Use Each

301: moving a document URL. A request for /docs/old-install should lead to /docs/install. With GET, there is no POST body to lose. This is the kind of permanent URL move covered by MDN’s 301 reference.

Illustrative exchange:

GET /docs/old-install HTTP/1.1
Host: example.com

HTTP/1.1 301 Moved Permanently
Location: /docs/install
Cache-Control: max-age=300
Content-Length: 0

308: moving a write endpoint. Suppose /api/v1/events is retired in favor of /api/events, which accepts the same payload. Redirect before processing the event at the old endpoint. 308 preserves the method and body; the destination receives the write.

Illustrative response to a POST at the old endpoint:

HTTP/1.1 308 Permanent Redirect
Location: /api/events
Cache-Control: no-store
Content-Length: 0

Keep the two jobs distinct in your routing code: either process the write or redirect it. A 308 after processing the event asks the client to submit the same event again at the destination.

Both statuses permit heuristic caching. That does not specify a universal browser TTL, guarantee storage of a POST response, or mean “forever.” RFC 9111 §4.2.2 leaves the heuristic algorithm unspecified. For a GET migration, the example’s max-age=300 chooses five minutes; it is a rollout choice, not a default. no-store instructs caches not to store the response (§5.2.2.5).

Google’s redirect documentation places 301 and 308 in the permanent category: Googlebot follows them, and the indexing pipeline uses the destination as a signal for canonicalization. This is a signal, not a promise that the old URL disappears immediately. Google also documents that an old URL can remain an alternate name and occasionally appear in results.

The Common Mistake

Testing only a GET, then shipping a 301 rule across an API. Both redirects look fine when you click the URL; a POST can lose its body under 301. Test the methods that actually reach that route.

The opposite mistake is using 308 after a form submission when you want the next request to fetch a receipt page. That is a 303 See Other flow: the redirect should lead to a retrieval, as defined in RFC 9110 §15.4.4.

Check On The Wire

Inspect the initial status, destination, and cache policy without following:

curl -sS -D - -o /dev/null https://example.com/docs/old-install

On your staging endpoint, trace a POST through the redirect:

curl -sS -v -L --data 'event=probe' \
  -o /dev/null https://example.com/api/v1/events 2>&1 |
  grep -E '^([<>] (POST|GET|HTTP/)|< [Ll]ocation:|< [Cc]ache-[Cc]ontrol:)'

Look for > POST at the first URL and the method at the second URL. Expect GET after 301 and POST after 308 with this command. Avoid -X POST and --post301: they override the method behavior being measured. These options and redirect rules are documented in the curl manual.

In Chrome DevTools, enable Network → Preserve log before submitting, then inspect each request’s method, payload, and Location. If cached routing obscures your change, repeat with Disable cache enabled while DevTools is open. See the Network reference.

Browse /search