HTTP 307 Temporary Redirect

Temporary Redirect

HTTP 307 is Temporary Redirect: the resource is temporarily at the URL in Location, and the client must repeat the same request there, with the same method and the same body. RFC 9110 section 15.4.8 forbids a user agent from changing the method when it follows a 307 automatically. That’s the whole difference from 302, where a POST usually comes back as a GET.

The exchange

POST /api/upload HTTP/1.1
Content-Type: application/octet-stream
Content-Length: 1048576

HTTP/1.1 307 Temporary Redirect
Location: https://uploads-2.example.com/api/upload

The client sends the POST and its whole megabyte again, to the new host. Browsers, fetch and curl -L keep the method for 307 and 308. One exception: a fetch whose body is a ReadableStream can’t be replayed, so following a 307 or 308 fails with a network error.

The 307 you didn’t send

Chrome’s DevTools shows 307 Internal Redirect when the browser upgrades http:// to https:// by itself, for a site on its HSTS list. Your server never sent that response, and changing your redirects won’t make it go away. The response header Non-Authoritative-Reason: HSTS gives it away.

Choosing a redirect

302 Found                temporary; POST may become GET
303 See Other            temporary; always GET the new URL (after a form POST)
307 Temporary Redirect   temporary; same method and body
308 Permanent Redirect   permanent; same method and body

Don’t use 307 to send a browser on after it posted a password. RFC 9700, the OAuth 2.0 security best current practice, forbids 307 in section 4.12 for redirects of requests that may carry user credentials, because the browser would resend the form fields to the next site; it recommends 303.

307 isn’t heuristically cacheable, so without Cache-Control every request goes to the original URL first. Google treats 307 like 302: it follows it and takes it as a weak hint that the target should be canonical. If the move is permanent, use 308, or 301 for plain pages.

Who sends it

FastAPI’s RedirectResponse defaults to 307, and so does Starlette’s redirect that adds or removes a trailing slash, so a POST /items to a route declared as /items/ comes back as a 307. Next.js redirects with permanent: false send 307.

In your stack

location /api/upload { return 307 https://uploads-2.example.com$request_uri; }
res.redirect(307, 'https://uploads-2.example.com/api/upload');      // Express
return RedirectResponse("https://uploads-2.example.com/api/upload")  # FastAPI: 307 by default
return redirect(url, code=307)                                       # Flask
http.Redirect(w, r, "https://uploads-2.example.com/api/upload", http.StatusTemporaryRedirect)

Try it in the HTTP Status Codes

307 Temporary Redirect

  1. 307Temporary Redirect3xx Redirection

    The resource is temporarily elsewhere, and the client must repeat the request at the new URL with the same method and the same body.

    When to use it

    Redirecting a POST or a PUT without turning it into a GET, which is what 302 often ends up doing.

    Common mistakes

    Expecting browsers to cache it, and using it where the move really is permanent: that is 308.

    Not cacheable by defaultRFC 9110

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.