HTTP 308 Permanent Redirect
308 is to 301 what 307 is to 302: the same meaning (moved permanently), but the client must not turn a POST into a GET when following it. It was introduced in 2015 by RFC 7538 and is now part of the core HTTP spec, RFC 9110.
308 Permanent Redirect
Permanently moved: repeat this and future requests at the Location URL without changing the method.
Who fixes it: Server config. Clients follow it. The site owner controls where it points and has to avoid redirect loops.
What 308 means
It's cacheable by default, so browsers remember it like a 301.
Modern platforms send a lot of 308s: Next.js permanent: true redirects, permanentRedirect() and trailing-slash normalisation all use it.
Source: RFC 9110 §15.4.9.
Where 308s come from
- Next.js trailing slash handling:
/about/redirects to/about(or the reverse withtrailingSlash: true) with a 308. permanent: trueredirects innext.config, orpermanentRedirect().- HTTP to HTTPS redirects on hosting platforms and CDNs that prefer method-preserving redirects.
- API versioning:
POST /v1/orderspermanently moved to/v2/orderswithout breaking clients that POST. - Redirect loops (
ERR_TOO_MANY_REDIRECTS) when a proxy terminates TLS and the app, which sees plain HTTP, keeps redirecting to HTTPS. Fix it by trustingX-Forwarded-Proto.
How to set up a 308
nginx
rewrite ... permanent sends 301, so use return 308 when you need the method preserved.
server {
listen 80;
server_name example.com;
return 308 https://example.com$request_uri;
}
# Keep POST/PUT bodies intact for API clients
location ~ ^/api/v1/(.*)$ {
return 308 /api/v2/$1$is_args$args;
}Express, behind a load balancer
Without trust proxy, req.secure is false for every request that arrived over HTTPS at the load balancer, and this middleware loops forever.
app.set('trust proxy', 1); // read X-Forwarded-Proto from the load balancer
app.use((req, res, next) => {
if (req.secure) return next();
res.redirect(308, `https://${req.headers.host}${req.originalUrl}`);
});Next.js
permanent: true sends 308.
// next.config.ts
const nextConfig = {
async redirects() {
return [{ source: '/docs/:path*', destination: '/guides/:path*', permanent: true }];
},
};
export default nextConfig;Check it
Look for a single hop that ends in 200.
curl -sIL http://example.com/docs/setup | grep -iE '^(HTTP|location)'Headers that matter with 308
- Location
- The new permanent URL.
- Cache-Control
- Like 301, a 308 may be cached indefinitely without it. Use a short
max-agewhile testing.
308 vs similar status codes
- 301 Moved Permanently: Same meaning, but browsers turn POST into GET. Equivalent for normal page moves.
- 307 Temporary Redirect: The temporary, method-preserving counterpart.
- 302 Found: Temporary and may change the method. The least precise of the four.
SEO impact of 308
Google treats 308 the same as 301: a strong signal that the target is the canonical URL, with ranking signals consolidated there. The usual redirect rules apply: one hop, the closest equivalent page, keep it for at least a year, and update internal links and sitemaps.
Related status codes
301Moved Permanently
The resource has a new permanent URL, given in the Location header.
307Temporary Redirect
Temporarily redirected: repeat the same request, with the same method and body, at the Location URL.
302Found
The resource is temporarily at a different URL; keep using the original URL for future requests.
Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.
Learn more
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
Frequently Asked Questions
Is 308 better than 301?▾
For APIs, yes, because it guarantees POST, PUT and DELETE requests are re-sent unchanged. For ordinary pages they're equivalent: browsers follow both and Google treats both as permanent. Internet Explorer 11 on Windows 7 and 8.1 doesn't understand 308, which is the only reason some sites still prefer 301.
Why does my Next.js site return 308 for trailing slashes?▾
Next.js normalises URLs. With the default trailingSlash: false, /about/ permanently redirects to /about with a 308; with trailingSlash: true it's the other way round. It's intentional, so each page has a single canonical URL.
How do I fix ERR_TOO_MANY_REDIRECTS with a 308 loop?▾
Usually TLS ends at a proxy or CDN, the app sees plain HTTP and redirects to HTTPS forever. Make the app trust the proxy's X-Forwarded-Proto header (app.set('trust proxy') in Express), or do the HTTPS redirect only at the edge. With Cloudflare, use the Full (strict) SSL mode instead of Flexible.