By SitemapFixer Team
Updated August 2026

308 Permanent Redirect: What It Means and When to Use It

Check every redirect and status code across your site in one scan.Analyze My Site Free

A 308 Permanent Redirect tells browsers and search engines two things at once: the URL has moved for good, and the HTTP method must not change on the way. If a client sends a POST to the old URL, it must send a POST — with the same body — to the new one. That method guarantee is the whole difference from a 301, which allows clients to rewrite a POST into a GET when they follow the redirect, and in practice most browsers do exactly that. For a normal page move the two codes behave the same. For an API endpoint or a form handler, the difference decides whether the request survives the redirect intact. This guide explains what a 308 does, how it compares to 301 and 307, how Google handles it, how to set one up, and how to debug it.

What Is a 308 Redirect?

A 308 Permanent Redirect is an HTTP response status code. The server returns it with a Location header that points to the new URL. The client — a browser, Googlebot, or an API consumer — is told to repeat the request at that new URL and to use the new URL for all future requests. The old address is retired.

The code was defined in RFC 7538 to complete a gap in the redirect family. The 307 code already existed as the strict, method-preserving version of the temporary 302. But the permanent 301 had no strict counterpart. Any client following a 301 was still allowed to switch a POST to a GET, which made permanent moves unsafe for anything that carried a request body. The 308 closed that gap: it is permanent like a 301 and method-preserving like a 307.

Think of the four main redirect codes as a two-by-two grid. One axis is permanence: is the move permanent or temporary? The other axis is method handling: may the client change the HTTP method, or must it keep it? The 301 and 302 sit on the loose side of that grid. The 307 and 308 sit on the strict side. Once you see the grid, picking the right code stops being guesswork.

308 vs 301: Method Preservation Is the Whole Point

Both codes signal a permanent move. Search engines treat them the same way. The only functional difference is what happens to the HTTP method when the client follows the redirect.

The original specification for 301 allowed clients to follow the redirect with a different method. Browsers took that permission and ran with it: nearly all of them convert a POST into a GET when they hit a 301, and they drop the request body in the process. For a user clicking links, that behavior is invisible and harmless — link clicks are GET requests anyway. For a form submission or an API call, it is destructive. The payload is gone, and the destination receives an empty GET it never expected.

A 308 removes that permission. The client must repeat the request with the same method and the same body. A POST arrives as a POST. A PUT arrives as a PUT. A DELETE arrives as a DELETE. Nothing about the request changes except the URL it is sent to.

So the decision rule is short. Moving a page that only ever receives GET traffic? A 301 works, and so does a 308 — they are interchangeable there. Moving a URL that receives POST, PUT, PATCH, or DELETE requests? Use a 308, because a 301 can silently break every non-GET client that follows it.

308 vs 307: The Permanent and Temporary Pair

The 307 and 308 are siblings. Both preserve the HTTP method strictly. The only difference between them is permanence.

A 307 Temporary Redirect says: use the new URL for this request only, and keep using the original URL in the future. Search engines keep the original URL indexed and do not move ranking signals to the destination. It is the right code for maintenance windows, A/B tests, and any redirect you plan to remove.

A 308 says the opposite: the original URL is finished, update all references to the new one. Search engines swap the indexed URL and consolidate signals to the destination. It is the right code for any move you do not plan to reverse.

A practical warning: because a 308 is a permanent signal, clients and caches remember it. If you are not certain the move is final, ship a 307 first, confirm everything works, then upgrade it to a 308. Downgrading a 308 after browsers have cached it is much harder than upgrading a 307 was.

301 vs 302 vs 307 vs 308 at a Glance

CodePermanenceMethod preservedTypical use
301 Moved PermanentlyPermanentNo — POST may become GETStandard page moves, URL cleanups, domain migrations
302 FoundTemporaryNo — POST may become GETShort-lived swaps, geo or session-based routing
307 Temporary RedirectTemporaryYes — POST stays POSTMaintenance pages, A/B tests, method-safe temporary moves
308 Permanent RedirectPermanentYes — POST stays POSTPermanent moves of APIs, form endpoints, and host canonicalization

When a 308 Actually Matters

API endpoint moves. This is the clearest case. Suppose your API moves from /v1/orders to /v2/orders and clients POST order data to it. A 301 would let well-behaved HTTP clients convert those POSTs to GETs and drop the body — orders would silently vanish. A 308 guarantees the POST, with its body, reaches the new endpoint. REST APIs that version or restructure their paths should default to 308 for retired routes.

Form endpoints. The same logic applies to any URL that receives form submissions. If you rename a contact form handler or move a checkout step, browsers that hit a 301 mid-submission can turn the POST into an empty GET at the destination. The user sees a broken page or a blank form, and you lose the data. A 308 keeps the submission intact through the move.

HTTPS and host canonicalization on modern platforms. Redirects that enforce your canonical host — www to apex, or HTTP to HTTPS at the server level — sit in front of every request, including POSTs. That is why platforms increasingly issue 308s for them: Vercel uses 308 for domain redirects and for permanent: true rules, and Next.js does the same. Note one HSTS detail: the internal redirect a browser performs for a site with a cached HSTS policy shows up in DevTools as a 307, not a 308 — that hop is browser behavior, not your server. But the first-visit server-side upgrade hop to HTTPS is yours, and a permanent code (301 or 308) is correct there.

Webhooks. If a webhook receiver URL changes, the services calling it send POSTs. Some webhook senders refuse to follow redirects at all, so update the URL at the source — but while both URLs exist, a 308 is the only redirect code that even has a chance of delivering the payload correctly.

When a 301 Is Fine

Most redirects on a normal website never see a non-GET request. Blog posts, docs pages, product pages, category pages — visitors and crawlers fetch them with GET, and there is no body to lose. For these, the 301 and the 308 produce identical outcomes, and the 301 has one small practical edge: it is the oldest redirect code, so ancient HTTP clients, exotic bots, and legacy monitoring tools all understand it. A few very old clients may not recognize 308 (it dates to 2015), though anything that matters for SEO handles it fine.

So do not rush to rewrite existing 301s as 308s. There is no SEO gain, and churning redirect rules risks introducing redirect chains or loops that cause real damage. Use 308 for new redirects where method preservation matters, keep working 301s as they are, and treat the two as equals everywhere else.

How Google Treats 308 Redirects

Google documents that it treats permanent redirects — 301 and 308 — as equivalent signals for crawling, indexing, and canonicalization. In both cases, Googlebot follows the redirect, treats the destination as the canonical URL, drops the old URL from the index over time, and consolidates ranking signals to the destination. Sites migrated behind 308s rank the same as sites migrated behind 301s.

This means the choice between 301 and 308 is an engineering decision, not an SEO decision. You do not earn extra ranking signal by picking one over the other. What matters to search engines is the permanence class: permanent codes (301, 308) consolidate signals to the destination, temporary codes (302, 307) leave signals at the origin.

Two operational notes. First, Search Console reports pages behind any redirect under the "Page with redirect" status — a 308 source URL showing there is expected and healthy, not an error. Second, your XML sitemap should list only final destination URLs. A sitemap entry that returns a 308 sends Google a mixed message: the sitemap says "index me" while the response says "I moved." Keep sitemaps pointing at URLs that return 200.

How to Set a 308 Redirect

Next.js. In next.config.js, permanent: true issues a 308 (and permanent: false issues a 307):

// next.config.js — permanent: true sends a 308
module.exports = {
  async redirects() {
    return [
      { source: '/old-path', destination: '/new-path', permanent: true },
    ];
  },
};

In the App Router, permanentRedirect from next/navigation also responds with a 308:

// app/old-path/page.tsx — server-side 308
import { permanentRedirect } from 'next/navigation';

export default function Page() {
  permanentRedirect('/new-path');
}

Nginx. The return directive accepts 308 directly:

# nginx — 308 Permanent Redirect
location = /old-path {
    return 308 /new-path;
}

# Redirect a whole prefix, preserving the rest of the URI
location /old-section/ {
    return 308 /new-section$request_uri;
}

Cloudflare. Redirect Rules (Rules → Redirect Rules) let you pick 308 from the status code dropdown for both single and bulk redirects. In a Worker, pass the status to Response.redirect:

// Cloudflare Worker — 308 redirect
export default {
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === '/old-path') {
      url.pathname = '/new-path';
      return Response.redirect(url.toString(), 308);
    }
    return fetch(request);
  },
};

Whatever the stack, redirect in one hop. Old URL straight to final URL — not through an intermediate that redirects again. And never fall back to a meta refresh redirect when a real HTTP status code is available.

Caching: Permanent Redirects Stick

Permanent redirect responses — 301 and 308 alike — are cacheable by default. A browser that has seen your 308 once will jump straight to the destination on the next visit without contacting your server at all. That is the desired behavior for a real permanent move: it saves a round trip on every future request.

It also has a sharp edge. If you ship a wrong 308 — a typo in the destination, or a redirect you later want to undo — browsers that cached it keep replaying it long after you fix the rule on the server. The fix is invisible to returning visitors until their cache expires or is cleared. This is the single most common source of confusion when testing redirects: the server is already correct, but your own browser keeps showing the old behavior.

Two defenses. First, test redirects with curl or a private window, never with a browser tab that has already followed the old rule — curl caches nothing, so it always shows what the server sends right now. Second, if a redirect might change, either use a 307 until it is final or attach an explicit Cache-Control: max-age to the redirect response to bound how long clients may replay it. A cached wrong redirect that loops back on itself is also one way sites end up with an ERR_TOO_MANY_REDIRECTS loop that only some visitors can see.

Debugging 308s with curl

The -I flag fetches headers only, which is all you need to see the status code and destination:

# See the status code and Location header for one hop
curl -I https://example.com/old-path

HTTP/2 308
location: https://example.com/new-path

# Follow the full chain and print every hop
curl -sIL https://example.com/old-path | grep -iE '^(HTTP|location)'

# Compact check: final status + where the first hop points
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' https://example.com/old-path

To verify that method preservation actually works end to end, send a POST and watch what arrives at the destination:

# Send a POST through the redirect; --post308 keeps the method
curl -sIL -X POST -d 'test=1' --post308 https://example.com/old-path

One curl quirk to know: when curl follows redirects with -L, older versions switch a POST to a GET unless you pass --post301, --post302, or --post308. Browsers preserve the method through a 308 automatically; curl just needs the flag. If your API "loses" its body only in scripted tests, this flag is usually the reason.

Check the whole chain, not just the first hop. A 308 that lands on a URL which then redirects again wastes crawl budget and slows users. Every permanent redirect should resolve to a single 200 in one hop. Run your site through the SitemapFixer scanner to map every redirect on the domain, flag chains and loops, and confirm your sitemap only lists final URLs.

Find Redirect Chains and Status Code Issues Instantly
Free sitemap and SEO analysis in 60 seconds
Analyze My Site Free

Related Guides