307 vs. 308 redirect
307 and 308 are the strict siblings of 302 and 301: they guarantee that a POST stays a POST. Here is what that means in practice, why you often see a 307 that your server never sent, and when a 308 is the better permanent redirect.
The short answer
- 307 Temporary Redirect is a temporary redirect, like 302, but the client must repeat the request with the same method and body.
- 308 Permanent Redirect is a permanent redirect, like 301, with the same guarantee: method and body are preserved.
So the relationship is simple: 302 → 307 and 301 → 308. The only thing that changes is how non-GET requests are handled. For search engines, 308 behaves like 301 and 307 behaves like 302.
Why 307 and 308 exist
The original HTTP specification intended 301 and 302 to keep the request method. Browsers did it differently: when they followed a 301 or 302 after a POST, they sent a GET to the new URL and dropped the request body. This became so widespread that RFC 9110 now explicitly allows user agents to change POST to GET for 301 and 302.
To have unambiguous codes again, HTTP/1.1 introduced 307 (temporary, method preserved) and 303 See Other (always switch to GET). The permanent counterpart 308 followed later in RFC 7538 and is now part of RFC 9110. Together they give you a complete set:
| Method may change to GET | Method always preserved | Always GET | |
|---|---|---|---|
| Permanent | 301 | 308 | – |
| Temporary | 302 | 307 | 303 |
What "method preservation" means in practice
Imagine a form that posts to /subscribe, and you move that endpoint to /newsletter/subscribe. With a 301, the browser follows with a GET request and without the form data. The user sees an empty form or an error, and the subscription is lost. With a 308, the browser sends the same POST with the same body to the new URL, and everything keeps working.
POST /subscribe HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
email=jane%40example.com
HTTP/1.1 308 Permanent Redirect
Location: https://example.com/newsletter/subscribe
POST /newsletter/subscribe HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
email=jane%40example.com
The same applies to PUT, PATCH and DELETE in REST APIs. If you move an API, a 307 or 308 is almost always the right choice, because API clients rely on the method arriving unchanged. Note that HTTP client libraries differ in how they follow redirects: some do not follow redirects for non-GET requests at all unless you enable it, so test with the clients your users actually run.
For ordinary page redirects, method preservation does not matter. Links, bookmarks and crawlers use GET, and GET stays GET with every 3xx code.
The 307 you did not send: HSTS and internal redirects
A very common question: "My server sends a 301 from HTTP to HTTPS, but Chrome shows a 307. Why?" The answer is usually HSTS (HTTP Strict Transport Security).
Once a browser has seen a Strict-Transport-Security header from your site over HTTPS, or if your domain is on the HSTS preload list, it knows that the site must only be loaded over HTTPS. When you then type or click an http:// URL, the browser does not even contact the server over HTTP. It rewrites the URL to https:// internally, and Chrome's developer tools display that as:
Status Code: 307 Internal Redirect
Non-Authoritative-Reason: HSTS
Key points about this "fake" 307:
- It is generated by the browser, not by your server. No network request to the HTTP URL takes place.
- It is not a problem and does not need fixing. It is actually the safest outcome, because no unencrypted request leaves the browser.
- Search engine crawlers and server-side tools do not see it. They see what your server really sends, typically a 301 or 308.
- Recent browsers may also upgrade HTTP to HTTPS on their own even without HSTS, which can show up as an internal redirect too.
If you want to know what your server actually answers, test from outside the browser. The redirect checker sends a fresh request without any HSTS state, so you see the real first hop, for example http://example.com/ answering with 301. Also check that your HTTPS responses send the Strict-Transport-Security header you intended, for instance with the HTTP header checker. More background in the guide on HTTP to HTTPS redirects.
307 vs. 308 vs. 301 vs. 302 compared
| Code | Permanent? | Method and body | Cached by default | Google canonical signal | Typical use |
|---|---|---|---|---|---|
| 301 | Yes | POST may become GET | Yes | Strong | Page and domain moves |
| 302 | No | POST may become GET | No | Weak | Temporary page redirects |
| 307 | No | Preserved | No | Weak | Temporary API or form redirects, HSTS in browsers |
| 308 | Yes | Preserved | Yes | Strong | Permanent API moves, HTTPS redirects |
According to Google Search Central, all of these pass PageRank. 308 is treated like 301 and is a strong signal that the target should become canonical. 307 is treated like 302. Like a 301, a 308 is cacheable by default, so browsers may remember it for a long time. Test with a 307 or 302 first if you are not sure yet.
When to use 307, when 308
Use a 308 when:
- an API endpoint or form handler moves permanently,
- you redirect HTTP to HTTPS and want POST requests to old HTTP URLs to arrive intact (Next.js and Vercel, for example, use 308 for their permanent redirects),
- you want a permanent redirect with unambiguous semantics for all methods.
Use a 307 when:
- an endpoint is temporarily served from somewhere else, for example during maintenance or a failover,
- a request must be retried against another host without changing the method.
Stick with 301/302 when you are only redirecting normal pages and want maximum compatibility with very old clients. Current browsers and search engines all understand 307 and 308, but some legacy software, for example old Internet Explorer versions on older Windows releases, did not handle 308 correctly.
And if you want the method to change, for example after a form submission to prevent double posts on reload (Post/Redirect/Get), use 303 See Other.
How to send a 307 or 308
Apache (.htaccess or virtual host), with mod_alias or mod_rewrite:
Redirect 308 /api/v1/ https://example.com/api/v2/
RewriteEngine On
RewriteRule ^subscribe$ /newsletter/subscribe [R=308,L]
nginx:
location ~ ^/api/v1/(.*)$ {
return 308 https://example.com/api/v2/$1$is_args$args;
}
location = /subscribe {
return 308 https://example.com/newsletter/subscribe;
}
Note that nginx's rewrite ... permanent always sends a 301 and redirect a 302. For 307 or 308 you need return 307 or return 308.
PHP:
header('Location: https://example.com/newsletter/subscribe', true, 308);
exit;
More examples for these servers are in the guides for .htaccess and nginx, or you can build rules with the .htaccess generator and nginx generator.
Testing 307 and 308 redirects
To check a redirect for GET requests, paste the URL into the redirect checker. You see every hop with its status code, so you can confirm that the server sends 308 rather than 301 and that there is no chain of several redirects.
To verify method preservation, send a real POST on the command line:
curl -i -L --data "email=test@example.com" https://example.com/subscribe
-L tells curl to follow redirects. With --data (and without -X POST), curl behaves like a browser: it switches to GET after a 301, 302 or 303, but keeps the POST after a 307 or 308. The output shows you directly whether your endpoint still receives the data. The MDN article on redirections is a good reference for how browsers handle each code.
Frequently asked questions
- Is a 308 redirect good for SEO?
Yes. Google treats a 308 like a 301: it passes PageRank and is a strong signal that the target URL should be indexed instead of the old one. You can use 308 anywhere you would use a 301.
- Why does Chrome show "307 Internal Redirect"?
Usually because of HSTS. The browser knows the site must be loaded over HTTPS and upgrades the URL itself, without sending a request over HTTP. The response header
Non-Authoritative-Reason: HSTSconfirms it. Your server did not send that 307, and crawlers never see it.- What is the difference between 302 and 307?
Both are temporary redirects. With a 302, browsers may change a POST request into a GET and drop the body. With a 307, the method and body must stay exactly the same. For GET requests they behave identically.
- Do all browsers support 308?
All current browsers do, as do search engine crawlers and common HTTP clients. Only very old software, such as outdated Internet Explorer versions on older Windows releases, had problems with 308. If you must support such clients, use a 301 for plain page redirects.
Related guides
-
301 vs. 302 redirect
A 301 says "this URL has moved for good", a 302 says "look over there for now". The difference sounds small, but it affects which URL Google indexes, how long browsers remember the redirect and how easy it is to undo.
-
301 redirect
A 301 redirect permanently sends visitors and search engines from an old URL to a new one. It is the standard tool for relaunches, domain moves and URL changes. Here is how it works, how to set it up and how to avoid the classic mistakes.
-
Meta refresh and JavaScript redirects
Not every redirect happens on the server. A meta refresh tag or a line of JavaScript can also send visitors to another URL. Here is how these client-side redirects work, how Google handles them and why they should only be your fallback.