The HTTP 308 Permanent Redirect status code was introduced in RFC 7538 (originally RFC 7238) to solve a critical limitation of 301 Moved Permanently. While 301 forces GET method conversion, 308 preserves both the request method and body during redirection - crucial for API endpoints and POST operations.
// Example of 308 response in Node.js
app.post('/old-endpoint', (req, res) => {
res.status(308)
.location('/new-endpoint')
.send('Permanent Redirect');
});
Modern browsers (Chrome 49+, Firefox 51+, Safari 11+, Edge 79+) fully support 308. For unsupported clients:
- Most modern user agents will treat 308 as 301 (with GET conversion)
- Some may follow the Location header as with 302/307
- A few may display the raw 308 response
When implementing 308 redirects:
# Nginx configuration example
location /deprecated/ {
return 308 https://$host/modern/$request_uri;
}
Key considerations:
- Always include Location header
- Maintain consistent URI schemes (HTTP/HTTPS)
- Consider adding Cache-Control headers
- Document redirect chains for API consumers
Verify 308 behavior with:
curl -v -X POST http://example.com/old-path \
-H "Content-Type: application/json" \
-d '{"test":"data"}'
Expected response:
HTTP/1.1 308 Permanent Redirect
Location: /new-path
When migrating REST APIs from v1 to v2:
# API Gateway 308 configuration
resources:
/v1/users:
post:
responses:
308:
description: Moved to v2
headers:
Location:
type: string
example: /v2/users
This preserves POST requests during version transitions while maintaining backward compatibility.
The HTTP 308 Permanent Redirect status code (RFC 7238) was introduced to solve a critical limitation of 301 Moved Permanently: the forced method change from POST to GET. Unlike 301, 308 preserves both the request method and body during redirection.
// Example of 308 redirect in Node.js Express
app.post('/old-endpoint', (req, res) => {
res.status(308).location('/new-endpoint').send();
});
Modern browsers (Chrome 61+, Firefox 51+, Edge 79+, Safari 11+) fully support 308. For unsupported clients:
- Most browsers will fall back to 302-like behavior (temporary redirect)
- Some may treat it as 301 (with unwanted method conversion)
- Legacy systems might fail completely
Consider these real-world scenarios where 308 shines:
# Nginx configuration for API versioning
location /v1/api {
return 308 https://api.example.com/v2/api$request_uri;
}
Key advantages:
- Maintains POST/PUT/PATCH request bodies
- Preserves HTTP headers during redirect
- Better for RESTful API migrations
For maximum compatibility, implement with fallbacks:
// PHP implementation with User-Agent check
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 8.0') !== false) {
header('Location: /new-path', true, 302);
} else {
header('Location: /new-path', true, 308);
}
308 redirects can be cached indefinitely by browsers, just like 301. Use Cache-Control headers carefully:
# Apache .htaccess example
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^old-path$ /new-path [R=308,L,NC]
Header set Cache-Control "max-age=31536000, immutable"
</IfModule>
Unlike 301/302, 308 prevents:
- CSRF token leakage via method downgrade
- Accidental data modification through GET requests
- Authorization header stripping in some implementations