HTTP 308 Permanent Redirect
HTTP 308 Permanent Redirect is a method-preserving permanent redirect. When a server returns 308, the client must repeat the original request — same method, same body — at the URL in the Location header, and update its records so future requests skip the original URL entirely. The critical distinction from 301: a 308 never converts POST to GET. This makes 308 the correct code for permanently moving API endpoints that accept non-GET methods.
Quick reference
| Code | 308 |
|---|---|
| Name | Permanent Redirect |
| Category | 3xx Redirect |
| Specification | RFC 7538 (also RFC 9110) |
| IANA status | Assigned |
| Method preserved? | Yes — POST stays POST, PUT stays PUT |
| Cacheable? | Yes — permanent redirect, cached like 301 |
| When to use | Permanent URL move where the HTTP method must not change |
The method-change problem with 301
The behavior of 301 with POST requests is a long-standing quirk of the HTTP specification. RFC 9110 states that when a client receives a 301 to a POST request, it may change the method to GET on the redirected request. The original RFC 2616 said the same thing, acknowledging that browsers had already established this behavior and codifying it. In practice, essentially all browsers and many HTTP libraries do change POST to GET when following a 301.
This is acceptable for web pages (where a POST form submission redirecting to a confirmation page is the intended pattern — the Post/Redirect/Get pattern), but it breaks API clients. If you permanently move a POST endpoint from /api/v1/payments to /api/v2/payments using 301, clients that follow the redirect will send a GET to /api/v2/payments instead of a POST. The request body is also discarded. The payment is never processed and the client may not realize why.
308 was standardized in RFC 7538 specifically to fill this gap: a permanent redirect that carries the same semantics as 307 (method and body preserved) rather than 301 (method may change).
Full request-response example
POST /api/v1/payments HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 42
{"amount": 1000, "currency": "USD", "to": "x"}
HTTP/1.1 308 Permanent Redirect
Location: https://api.example.com/api/v2/payments
Cache-Control: max-age=31536000
<!-- Client follows redirect -->
POST /api/v2/payments HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 42
{"amount": 1000, "currency": "USD", "to": "x"}
The method is POST on both the original request and the redirected request. The request body is sent again at the new URL. On subsequent requests, the client goes directly to /api/v2/payments without hitting /api/v1/payments at all (because it cached the 308).
Configuring 308 in common servers
nginx:
# Permanent method-preserving redirect return 308 https://api.example.com/api/v2$request_uri; # Or in a rewrite rule rewrite ^/api/v1/(.*)$ /api/v2/$1 permanent; # Note: nginx "permanent" keyword sends 301. Use return 308 for method preservation.
Apache:
Redirect permanent /api/v1/ https://api.example.com/api/v2/ # Apache Redirect directive sends 301. For 308: RedirectMatch 308 ^/api/v1/(.*)$ https://api.example.com/api/v2/$1
Express.js:
app.use('/api/v1', (req, res) => {
const newUrl = 'https://api.example.com/api/v2' + req.path;
res.redirect(308, newUrl);
});
Cloudflare Redirect Rules: In the Cloudflare dashboard, set a redirect rule with status code 308. Cloudflare correctly sends 308 and does not modify the method for downstream requests it forwards.
Caching behavior
A 308 response is cacheable by default, like 301. The browser or HTTP client caches the redirect and goes directly to the new URL on subsequent requests without contacting the original URL. The cache lifetime follows the Cache-Control or Expires header in the 308 response. If no caching header is present, the client may cache indefinitely.
To prevent caching (if you might change the redirect later), include Cache-Control: no-store in the 308 response. For a permanent redirect you are confident in, Cache-Control: max-age=31536000 (one year) is a reasonable choice that reduces load on the old endpoint.
To undo a cached 308: the original URL must serve a non-redirect response again. Clients that cached the redirect will need to clear their cache or wait for the cache TTL to expire before they will re-request the original URL.
Client compatibility
308 was standardized in 2015 (RFC 7538). Modern browser and library support is good but not universal across all environments:
| Client | 308 support | Notes |
|---|---|---|
| Chrome, Firefox, Edge, Safari (current) | Yes | Full support, method preserved |
| curl (7.56+) | Yes | --location follows 308 preserving method |
| Python requests (2.x) | Yes | Follows 308, preserves method |
| Node.js http module | Manual | Does not auto-follow redirects; handle in app code |
| Java HttpURLConnection | No | Falls back to 300 behavior; use Apache HttpClient |
| Internet Explorer 11 | No | Treats as generic 3xx, may change method |
For API-to-API communication where you control both ends, verify your HTTP client library explicitly. For browser-facing form submissions, 308 is safe in all modern browsers.
308 vs 301 vs 307
| Code | Permanent? | Method preserved? | Body preserved? | Cached? |
|---|---|---|---|---|
| 301 | Yes | No (POST may become GET) | No | Yes |
| 307 | No | Yes | Yes | No (by default) |
| 308 | Yes | Yes | Yes | Yes |
Frequently asked questions
What is the difference between 301 and 308?
Both are permanent redirects that clients should cache. The difference is method handling: with 301, clients may (and commonly do) change POST to GET on the redirected request. With 308, clients must repeat the request with the same method and body. Use 308 when moving POST, PUT, or PATCH endpoints permanently.
Is HTTP 308 cached by browsers?
Yes. Like 301, a 308 response is cached by browsers and HTTP clients. The browser will not re-request the original URL on subsequent visits; it will go directly to the redirected URL. To make a 308 non-cacheable, add Cache-Control: no-store.
Do all HTTP clients support 308?
Most modern browsers and HTTP libraries support 308. Older clients (pre-2015 browsers, legacy curl versions, some HTTP client libraries) may not recognize 308 and will fall back to treating it as 300. Test your actual client stack before deploying 308 for non-browser traffic.
When should I use 308 instead of 307?
Use 308 when the redirect is permanent and you want clients to update their bookmarks and cached URLs. Use 307 when the redirect is temporary and clients should continue hitting the original URL on future requests. Both preserve the request method.
Related guides
HTTP 301 Moved Permanently · HTTP 307 Temporary Redirect · HTTP 302 Found
Comparisons
Standards reference
Definitions from the IANA HTTP Status Code Registry and RFC 7538. Human-readable guidance by ErrorLookup. · HTTP 308 quick reference →