HTTP status code · Redirection (3xx)
307 Temporary Redirect
307 Temporary Redirect means the resource is temporarily at the URL in Location and the client must repeat the request there with the same method and body. You will often meet it in Chrome DevTools as "307 Internal Redirect", which is the browser upgrading an http:// link to https:// because of HSTS, before any request leaves your machine.
| Class | 3xx, Redirection |
|---|---|
| Defined in | RFC 9110 §15.4.8 |
| Cacheable by default | Only with explicit Cache-Control or Expires |
| Safe to retry | Repeat the same request, same method and body, at Location |
| Relevant headers |
|
What 307 means
RFC 9110, section 15.4.8, adds one rule on top of 302: the user agent must not change the request method when it follows the redirect automatically. A POST with a JSON body is re-sent as a POST with the same body to the new location. The client should keep using the original URL for later requests, and without explicit caching headers the redirect is not stored.
Chrome also reports redirects it performs internally as 307. When a site is on the HSTS list, an http:// URL is rewritten to https:// inside the browser, and DevTools shows a synthetic "307 Internal Redirect" with the header Non-Authoritative-Reason: HSTS. No server sent that response, so there is nothing to fix on the server.
Several frameworks default to 307: Next.js redirect() and NextResponse.redirect(), FastAPI’s RedirectResponse, and Starlette’s automatic trailing-slash redirect. That makes it easy to ship a 307 where a permanent redirect was intended.
When to use it
- Temporarily sending traffic that includes POST or PUT to another server: a regional endpoint, a maintenance instance, a different upload host.
- Any temporary redirect in an API, where silently turning a POST into a GET would break the call.
- Short-term moves of normal pages too; for GET requests it behaves exactly like 302.
Common causes
If you are visiting the site
- DevTools shows "307 Internal Redirect": the browser’s HSTS policy upgraded http:// to https://, which is expected.
- A site answering every request with a 307 to a login, region or consent page it wants you to see first.
If you run the server
- Framework defaults: a permanent move written with Next.js redirect() or NextResponse.redirect() goes out as 307, so search engines treat it as temporary.
- FastAPI and Starlette redirect /items to /items/ (or the reverse) with 307 when the route was declared with the other form, doubling the request count for API clients.
- An http to https redirect for API traffic: it works with 307, but costs a round trip per call and the first request, body included, has already crossed the network unencrypted.
How to fix it
If you are visiting the site
- Nothing to do for the HSTS internal redirect. For a loop of 307s, clear the site’s cookies and try again.
If you run the server
- Use permanentRedirect() or a next.config redirect with permanent: true when the move is permanent; both send 308.
- Declare API routes with the exact path clients call, or create the app with FastAPI(redirect_slashes=False), so clients stop paying for the extra hop.
- Point API clients at https:// directly and enable HSTS instead of relying on a redirect.
How to send 307
// Uploads go to the EU host while the main one is being migrated
app.post('/uploads', (req, res) => {
res.redirect(307, 'https://upload-eu.example.com/uploads');
});// app/uploads/route.ts
export async function POST() {
return Response.redirect('https://upload-eu.example.com/uploads', 307);
}
// redirect() from next/navigation and NextResponse.redirect()
// also default to 307.mux.HandleFunc("POST /uploads", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://upload-eu.example.com/uploads", http.StatusTemporaryRedirect) // 307
})from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.post("/uploads")
def uploads():
# 307 is RedirectResponse's default; spelled out for clarity
return RedirectResponse("https://upload-eu.example.com/uploads", status_code=307)location /uploads {
return 307 https://upload-eu.example.com$request_uri;
}Commonly confused with
- 307 vs 302
- Both are temporary; 302 lets clients turn POST into GET and browsers do, while 307 forbids any method change.
- 307 vs 308
- 308 is the permanent twin: same method-preserving rule, but cacheable by default and treated by Google as a permanent move.
- 307 vs 303
- 303 forces the follow-up to be a GET for a result page; 307 forces it to be the same request again.
Frequently asked questions
- What is "307 Internal Redirect" in Chrome?
- A redirect Chrome performs itself, usually because the site is on the HSTS list, so http:// is upgraded to https:// before any network request. DevTools labels it with Non-Authoritative-Reason: HSTS. The server never sent it and it needs no fix.
- What is the difference between 302 and 307?
- Both mean a temporary move. With 302, browsers switch a POST to GET and drop the body; with 307 they must repeat the POST with the same body. For GET requests the two behave identically.
- Is a 307 redirect bad for SEO?
- Google treats 307 as equivalent to 302, a temporary redirect, so the original URL stays in search results. That is correct for temporary moves and a mistake for permanent ones, which should use 301 or 308.
- Why does Next.js return 307 for my redirect?
- redirect() from next/navigation and NextResponse.redirect() default to 307, and redirect() in a Server Action returns 303. Use permanentRedirect() or permanent: true in next.config for a 308.
Last reviewed by Arielton Oberek.