SiteError.comYour friendly guide to HTTP status codes
Status CodesBlog
  1. Home
  2. Blog
  3. Understanding HTTP 308 Permanent Redirect: Method-Preserving Redirects Done Right

Understanding HTTP 308 Permanent Redirect: Method-Preserving Redirects Done Right

May 20, 20268 min read
3xxRedirection

The 308 Permanent Redirect is the modern, method-preserving sibling of the 301. For an ordinary "this page moved" redirect, the two are interchangeable. But the moment a POST, PUT, or DELETE is involved, the difference becomes load-bearing — and choosing 301 out of habit can quietly turn a POST into a GET and break your API. Our 301 redirects post covers the SEO mechanics of permanent redirects in depth; this one is about the part 301 can't guarantee, and when you actually need 308.

What Is a 308?

A 308 tells the client the resource has permanently moved to the URL in the Location header — and, critically, that it must repeat the request exactly as it was, including the method and body.

The 308 (Permanent Redirect) status code indicates that the target resource has been assigned a new permanent URI and any future references to this resource ought to use one of the enclosed URIs. [...] This status code is similar to 301 (Moved Permanently), except that it does not allow changing the request method from POST to GET. — RFC 7538, Section 3

A bare 308 looks like this:

HTTP/1.1 308 Permanent Redirect
Location: /new-permanent-endpoint

Same shape as a 301 — the difference is entirely in how the client is required to behave afterward.

308 vs 301: The Method-Preservation Difference

Here's the whole point of 308 in one paragraph. A 301 permits the client to change the request method from POST to GET when it follows the redirect. This isn't a bug — it's baked into how browsers have behaved since the 1990s, when many clients silently downgraded redirected POSTs to GETs. A 308 forbids that: the method and body must survive the redirect untouched.

So:

  • Page-to-page GET redirects — 301 and 308 are functionally identical. The browser was going to issue a GET either way.
  • POST/PUT/DELETE redirects — they diverge. A 301 might turn your POST /api/orders into GET /api/orders (dropping the body entirely); a 308 guarantees it stays POST /api/orders with the body intact.

If you've ever seen a form submission or API call mysteriously lose its payload after a redirect, an over-eager 301 (or 302) is the usual suspect. 308 is the fix.

The Full Redirect Matrix

Two axes decide which redirect you want: is the move permanent, and must it preserve the method?

StatusPermanent?Preserves method?Use when
301 Moved PermanentlyYesNo (may →GET)Permanent move, GET pages, SEO migrations
302 FoundNoNo (may →GET)Temporary move (A/B test, maintenance)
303 See OtherNoNo (forces GET)Post/Redirect/Get after a form submission
307 Temporary RedirectNoYesTemporary move that must keep the method
308 Permanent RedirectYesYesPermanent move that must keep the method (APIs)

Read the bottom two rows as the method-preserving pair: 307 is to 302 as 308 is to 301. And 303 is the deliberate odd one — it forces a GET, which is exactly what you want for the Post/Redirect/Get pattern after a form submit.

When to Use 308

Reach for 308 when the move is permanent and the method matters:

  • Permanently relocated API endpoints that accept POST/PUT/PATCH/DELETE. Clients should update their references, and their request bodies must survive the hop in the meantime.
  • HTTPS upgrades for non-GET traffic. If you redirect all HTTP to HTTPS and any of that traffic is POST (webhooks, form posts, API calls), a 301 risks dropping the body. 308 keeps it.
  • Permanent URL restructuring where you can't assume every caller is using GET.

When a plain 301 is the better call:

  • GET-only page moves — content migrations, vanity URLs, trailing-slash normalization. 301 is universally understood by every crawler, proxy, and ancient client, and method preservation is irrelevant for a GET.

For SEO, the two are equivalent (more below), so the deciding factor is almost always what HTTP methods hit this URL.

Implementation Examples

NGINX

# Permanent, method-preserving redirect
location /old-api {
  return 308 /new-api;
}
 
# HTTP→HTTPS keeping POST bodies intact
server {
  listen 80;
  server_name api.example.com;
  return 308 https://$host$request_uri;
}

Apache (.htaccess)

# Single endpoint
Redirect 308 /old-api https://example.com/new-api
 
# With rewrite rules
RewriteEngine On
RewriteRule ^old-api/(.*)$ https://example.com/new-api/$1 [R=308,L]

Next.js (next.config.js)

This one trips people up — including a simplification in our own 301 post. Next.js's redirects() does not emit 301/302. It emits the method-preserving codes:

module.exports = {
  async redirects() {
    return [
      {
        source: "/old-api/:path*",
        destination: "/new-api/:path*",
        permanent: true, // → 308 (NOT 301)
      },
      {
        source: "/beta",
        destination: "/preview",
        permanent: false, // → 307 (NOT 302)
      },
    ];
  },
};

So permanent: true gives you a 308 and permanent: false gives you a 307. If you specifically need a 301 (say, for a tool that mishandles 308), use statusCode: 301 instead of permanent — but for almost all cases, Next.js's 308/307 default is the more correct behavior.

Node.js / Express

app.all("/old-api", (req, res) => {
  res.redirect(308, "/new-api");
});

Use app.all (not app.get) so the redirect applies to every method — the whole reason you picked 308 is to handle non-GET requests.

Caching Behavior

Like 301, a 308 is treated as permanent and cached aggressively. Once a browser or proxy sees a 308 from /old to /new, it may skip the server entirely on future requests and go straight to /new — by design.

That carries the same recovery trap as 301: if you ship a wrong 308, fixing it server-side won't immediately help clients who already cached the bad redirect. Protect yourself the same way:

  • Test redirects in an incognito/private window before deploying.
  • Set a sane Cache-Control so caches will eventually re-check:
HTTP/1.1 308 Permanent Redirect
Location: https://example.com/new-api
Cache-Control: max-age=86400

A 24-hour window gives you the permanence benefits while leaving room to recover from a mistake.

SEO

For search engines, 308 and 301 are equivalent. A 308:

  • De-indexes the old URL and replaces it with the destination
  • Passes link equity (ranking signals) to the new URL
  • Acts as a strong canonical signal

Here's the subtle part: crawlers issue GET requests, so the method-preservation feature that makes 308 special is invisible to them. That means you should never choose 308 over 301 for SEO reasons — they behave identically in the index. Choose 308 over 301 for user and API behavior (keeping POST bodies intact), not for rankings.

Browser & Client Support

308 was standardized in 2015 (RFC 7538) and is now universally supported across modern browsers, HTTP libraries, and CDNs. The historical "not every client understands 308" caveat is effectively obsolete for the public web.

The exception worth a thought: very old or minimal HTTP clients (legacy embedded devices, hand-rolled clients, ancient libraries) may not recognize 308 and could fail rather than follow it. If you're redirecting traffic from an unknown long tail of such clients, that's the one scenario where falling back to 301 — and accepting the method-change risk — can be the pragmatic choice.

Common Pitfalls

  1. Using 301 for an API endpoint that needs POST preserved. The redirect "works" in a browser test (GET), then silently drops bodies for real POST traffic in production. Use 308.
  2. Assuming permanent: true means 301. In Next.js it's a 308; in other frameworks it varies. Check what your framework actually emits before relying on a specific code.
  3. Redirect chains and loops. 308 caches as hard as 301, so a chain of permanent hops is even stickier. Point the original redirect straight at the final destination — see the 301 post for detection techniques with curl -sIL.
  4. Dropping query strings. Make sure your 308 rule carries through ?utm_source=... and friends, or you'll lose attribution data on every redirected request.
  5. Forgetting internal links. As with any redirect, update your own links to point at the new URL directly. The redirect is a safety net for callers you don't control, not a substitute for fixing references you do.

Wrapping Up

308 is "301 that keeps its promises about the HTTP method." The rules of thumb:

  • For GET page moves, 301 and 308 are interchangeable — pick 301 for maximum tooling compatibility
  • For permanent moves of POST/PUT/DELETE endpoints, use 308 so the method and body survive
  • Remember the matrix: 307 is the temporary method-preserving redirect, 308 is the permanent one
  • Next.js redirects() emits 308 (permanent: true) and 307 (permanent: false) — not 301/302
  • 308 caches aggressively like 301; test in incognito and set a sane Cache-Control
  • For SEO, 308 == 301; choose 308 for client behavior, never for rankings

For more, see our pages on 308 Permanent Redirect, 301 Moved Permanently, and 307 Temporary Redirect. The companion understanding 301 redirects post goes deep on migrations and SEO equity, and you can compare 301 vs 308 side by side.

Related Status Codes

🚪300Multiple Choices📦301Moved Permanently🔀302Found👀303See Other💾304Not Modified🕵️305Use Proxy↪️307Temporary Redirect🏠308Permanent Redirect
Back to Blog

Popular Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 302 Found
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

Compare Codes

  • 401 vs 403
  • 301 vs 302
  • 404 vs 410
  • 500 vs 502
  • Compare any codes →

Categories

  • Informational
  • Success
  • Redirection
  • Client Error
  • Server Error
  • NGINX
  • Cloudflare
  • AWS ELB
  • Microsoft IIS

Tools

  • Cheat Sheet
  • Status Code Quiz
  • URL Checker
  • API Playground
  • Blog

© 2026 SiteError.com. All rights reserved.