How HTTP Works
3xx · Redirection
< HTTP/1.1 302 Found

HTTP 302 Found: Temporary Redirect

Learn what 302 redirect means, when to use temporary vs permanent redirects, and how 302 differs from 301, 307, and 308.

Reviewed 5 min readbeginner2 sourcesTry itMarkdown
Cacheable
Only with explicit freshness
Client action
Follow Location for this request only
Usually sent by
Origin app, reverse proxy or CDN rule
Spec
RFC 9110 §15.4.3
Often confused with
307, 303, 301
On this page

TL;DR: 302 Found means the requested URL temporarily redirects to another URL. Follow Location; if the redirect is unexpected or loops, inspect the first response and the rule that sends it. Use 307 when the request method and body must survive.

What it means

The HTTP exchanges below are illustrative. A request to /promo receives a temporary destination in Location.

GET /promo HTTP/1.1
Host: example.com

HTTP/1.1 302 Found
Location: /current-sale

302 is not heuristically cacheable. Explicit freshness such as Cache-Control: max-age=600 can make a GET redirect reusable for ten minutes (RFC 9111 section 4.2.2). Express’s res.redirect('/x') defaults to 302; pass a status explicitly for a permanent move.

What you see in your client

An HTTP 302 is a redirect response. Automatic following can hide it behind the destination’s status, so inspect the first response when your browser and script appear to disagree.

  • Axios: in Node’s HTTP adapter, maxRedirects: 0 exposes the redirect. With the default success predicate, it rejects with AxiosError: Request failed with status code 302. Accept the code in validateStatus to inspect response.headers.location. The behavior comes from the HTTP adapter, defaults and error construction.
  • Browser fetch: follows redirects by default and returns the final response. With redirect: 'manual', a browser returns type: 'opaqueredirect', status: 0 and ok: false, with hidden headers. A directly exposed 302 would also have ok: false, since only 200–299 passes. Use the Network panel or curl for the actual Location; these rules are in the Fetch Standard.
  • Python requests: requests.get(url, allow_redirects=False) exposes 302; raise_for_status() returns normally and response.ok is True. Inspect response.headers['Location']. Its error guard raises for 4xx/5xx, while GET follows by default.
  • curl -f: accepts 302; -L enables following. Without -L, inspect the headers using -i. The manual reserves failure code 22 for HTTP responses of 400 or greater.
  • .NET: set HttpClientHandler.AllowAutoRedirect = false to inspect the first response. Calling EnsureSuccessStatusCode() then throws HttpRequestException; with English resources and reason phrase Found, the message is Response status code does not indicate success: 302 (Found). See the redirect setting, guard and message resources.
  • Spring: WebClient.retrieve() uses WebClientResponseException for 4xx/5xx by default. RestTemplate’s default handler likewise treats those ranges as errors; a 302 alone produces neither that exception nor HttpClientErrorException.

Which temporary redirect?

SituationUseWhy
After a form POST, send the user to a result page303Always becomes GET; the precise code for Post/Redirect/Get
Temporary move and the method/body must survive307POST stays POST
Plain GET page redirected temporarily302Works everywhere; method change is harmless for GET

RFC 9110 allows POST to become GET on 302. The Fetch redirect algorithm makes that change for browser requests; 307 preserves the method and body. See 302 vs 307.

Implementation

Express

// Login redirect: encode user input and keep the target on-site
app.get('/dashboard', (req, res, next) => {
  if (!req.user) {
    return res.redirect(302, `/login?returnTo=${encodeURIComponent(req.originalUrl)}`)
  }
  next()
})

// After login: validate returnTo before redirecting (open-redirect defense)
app.post('/login', (req, res) => {
  const target = String(req.query.returnTo || '/')
  const safe = target.startsWith('/') && !target.startsWith('//') && !target.startsWith('/\\')
  res.redirect(303, safe ? target : '/')
})

An unvalidated returnTo lets an attacker craft https://yoursite.com/login?returnTo=https://evil.example, which turns your domain into a phishing launchpad. Allow only same-site relative paths or an explicit allowlist of hosts.

nginx

location = /promo {
    return 302 /current-sale;
}

Common causes by stack

Express: res.redirect('/login') defaults to 302. Check authentication middleware when an API suddenly returns a login page; use an API’s documented authentication response instead of sending its client through an HTML login flow.

Django: redirect('/login/') defaults to 302. Check the view or authentication wrapper that chooses the target. Use permanent=True for a permanent move; use preserve_request=True when the method/body must remain intact, which selects 307 for a temporary redirect.

Laravel: redirect()->to('/dashboard') uses 302 by default; the Redirector source declares $status = 302. Trace that response call when a successful form POST appears as “302 Found” in the Network panel, then inspect the following GET.

Rails: redirect_to defaults to 302. Set status: :see_other after a form mutation when the destination is a result page, and inspect the session cookie if the destination sends the user straight back to login.

nginx / Apache: nginx’s return 302 /current-sale; explicitly emits the temporary redirect. Apache’s Redirect /promo /current-sale uses 302 when the status is omitted. Inspect the loaded virtual host or location for a matching rule before changing application code.

AWS ALB: a listener redirect action with StatusCode: HTTP_302 returns 302 before the request reaches a target. Check the matching listener rule and its protocol, host, port and path substitutions; at least one must change to avoid a loop.

Kubernetes ingress-nginx: nginx.ingress.kubernetes.io/temporal-redirect returns 302 instead of forwarding to the upstream. Inspect that Ingress annotation when the application’s logs show no request.

FastAPI’s Starlette RedirectResponse defaults to 307, so a FastAPI 302 requires an explicit status or another layer’s redirect rule. Inspect RedirectResponse(url, status_code=302) before attributing the response to the framework’s default.

SEO behavior

Google follows a 302 but does not use that temporary redirect to select the destination as canonical. Other canonicalization signals can affect which URL appears in search. Use 301 or 308 for a permanent move, and retain 302 for a destination you expect to revert.

Debugging

curl -sI https://example.com/promo
curl -sIL -o /dev/null -w '%{http_code} -> %{url_effective}\n' https://example.com/promo
# See what a POST turns into (curl -L switches POST to GET on 301/302/303 unless --post302)
curl -si -L -d 'a=1' https://example.com/form

Chrome reports ERR_TOO_MANY_REDIRECTS when a chain loops. Inspect cookies during a repeated dashboard-to-login bounce; for a scheme loop, compare the edge and origin HTTPS rules. Cloudflare documents loops caused by Flexible mode and origin HTTPS redirects. Trace the chain with the redirect audit tool.

Inspect a redirect without following it

For an API call, use curl -si with the actual request method and body, then read Location before enabling -L. A HEAD probe with -I can exercise a different route from your POST. Repeat the request with -L -v to inspect the outgoing methods in a test environment.

In the command above, -d selects POST. Adding -X POST would force that method string on subsequent requests with -L, masking the normal POST-to-GET change. Use --post302 only when deliberately testing curl’s method-preserving behavior; the curl manual distinguishes it from the default.

Post/Redirect/Get

PRG makes a result-page reload repeat a GET instead of submitting the original POST again. The server handles the POST, then redirects the browser to a GET page; reloading that page repeats only the GET. Fetch changes POST to GET on 302, but 303 states the intended retrieval explicitly (RFC 9110 section 15.4.4).

POST /posts HTTP/1.1
Host: example.com
Content-Length: 0

HTTP/1.1 303 See Other
Location: /posts/123

Frequently asked questions

What does 302 redirect mean?

A 302 tells browsers the resource is temporarily at a different URL. Keep using the original URL—it will work again later.

What is the difference between 301 and 302?

301 marks a permanent move and gives Google a strong canonicalization signal. 302 marks a temporary move, so clients should keep using the original URL for future requests.

Does 302 affect SEO?

Google follows 302 redirects but does not use them to select the destination as canonical. Use 301 or 308 when the move is permanent and the destination should become canonical.

When should I use 302 vs 307?

Browsers using Fetch change POST to GET when following 302. 307 guarantees method preservation, and 303 is the explicit choice for redirecting a POST to a GET result page.

Sources

  1. MDN Web Docs: 302 Founddeveloper.mozilla.org
  2. RFC 9110: 302 Foundrfc-editor.org

Keep going

Browse /search