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
| Behavior | 301 Moved Permanently | 308 Permanent Redirect |
|---|---|---|
| Meaning | Resource has a new permanent URL | Resource has a new permanent URL |
| Redirected GET | Remains GET | Remains GET |
| Redirected POST in browser Fetch | Becomes GET; body discarded | Method and replayable body preserved |
| HTTP method rule | POST-to-GET conversion is permitted | Method must be preserved when following automatically |
| Cache eligibility | Heuristically cacheable, subject to method and cache controls | Same |
| Google Search | Permanent redirect; destination is a canonicalization signal | Same 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.
Caching And Google Search
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.
Related
- 301 Moved Permanently and 308 Permanent Redirect
- 301 vs 302 for permanent versus temporary moves
- Cache-Control for explicit redirect cache policy