Choosing 308 vs 301 for Method-Preserving Redirects
Problem Statement
You are redirecting an endpoint that receives POST requests — a form action, webhook, or API path — and need to know whether a 301 or a 308 is correct. A 301 permits clients to convert the follow-up request to GET, silently dropping the POST body, whereas a 308 guarantees the method and body are preserved. This page sits under 301 vs 302 Decision Trees.
When to Use This Approach
- The redirected path accepts POST, PUT, PATCH, or DELETE — not just GET page views.
- You are migrating a form action URL, webhook receiver, or API endpoint.
- A client reports that data “disappears” after a redirect — a classic 301 method downgrade.
- You want a permanent redirect that is byte-for-byte safe for request bodies.
- You are choosing the
permanentflag in a framework that emits 308 (Next.js, Nuxt).
Step-by-Step Instructions
1. Decide Method Preservation First
Ask whether the endpoint ever receives a non-GET method. If it only serves page views, 301 is fine and is the most widely cached. If it can receive POST/PUT/PATCH/DELETE, choose 308 so the method and body survive the redirect.
GET-only page move -> 301 (permanent, method may downgrade — irrelevant for GET)
POST/API/webhook move -> 308 (permanent, method + body preserved)
2. Configure 308 in Nginx
Nginx return accepts any status code, so emit 308 explicitly. Append $is_args$args to carry the query string, and keep the rule in the matching location.
# Permanent, method-preserving redirect for a moved form/API endpoint
location = /submit {
return 308 /api/v2/submit$is_args$args;
}
3. Configure 308 in Apache
Apache RewriteRule takes a numeric status with R=308. Use it where the moved path may receive POST.
# 308 keeps the POST body intact across the move
RewriteEngine On
RewriteRule ^/submit$ /api/v2/submit [R=308,L,QSA]
4. Map Framework “permanent” Flags Correctly
Modern frameworks emit 308 for permanent redirects. If a tool’s permanent: true produces 308 (Next.js, Nuxt routeRules can target either), confirm it matches your method requirement; for stack-specific syntax see CMS & Framework Routing Changes.
// next.config.js — permanent: true emits 308, which is what an API move wants
{ source: '/submit', destination: '/api/v2/submit', permanent: true }
Worked Example
A webhook receiver moves from /submit to /api/v2/submit. With a 301, some clients re-issue the follow-up as GET and the JSON body is lost:
$ curl -sIL -X POST https://www.example.com/submit
HTTP/2 301
location: https://www.example.com/api/v2/submit
# client re-sends as GET -> body dropped, 400 at target
Switching the rule to 308 preserves the POST and its body:
$ curl -sIL -X POST https://www.example.com/submit
HTTP/2 308
location: https://www.example.com/api/v2/submit
HTTP/2 200
The receiver now gets the original POST body at the new path in a single hop.
Verification
- Confirm the status code is 308, not 301:
curl -sI -X POST https://www.example.com/submit | grep -i '^HTTP'. - Send a real POST body with
curl -X POST -d '{"k":"v"}'and confirm the target processes it (200/201), proving the body survived. - Check the query string is preserved on the
Locationheader for parameterised endpoints.
FAQ
What is the difference between 301 and 308? Both are permanent. A 301 historically allows clients to change the follow-up request method to GET, which drops a POST body; a 308 forbids that, preserving the method and body. For GET-only page moves they behave the same for SEO.
Do search engines treat 308 like 301? Yes. Google treats 308 as a permanent redirect equivalent to 301 for ranking and consolidation. Use 308 specifically when method and body preservation matters; use 301 for ordinary GET page moves where it has the widest caching support.
Related
← Back to 301 vs 302 Decision Trees