Response header
< Location: /articles/42Location
Learn how the Location header specifies redirect URLs or the location of newly created resources. Essential for 201, 301, 302, and other redirect responses.
- Direction
- Response
- Category
- Redirection
- Spec
- RFC 9110 §10.2.2
On this page
TL;DR:
Locationis the response header that tells the client where to go: the redirect target on a 3xx, or the URL of the new resource on a201 Created. Relative URLs like/accountare fine. The status code decides the rest: whether the redirect is permanent, and whether a POST stays a POST (307, 308) or may become a GET (301, 302, 303).
What is Location?
The value is a URI reference, absolute or relative. It redirects nothing on its own: a 200 OK with Location is still a 200. On a 201 Created, it tells the client where the new resource lives, and the client doesn’t navigate there. See RFC 9110 §10.2.2.
How It Works
A redirect with an empty body (examples on this page are trimmed):
HTTP/1.1 307 Temporary Redirect
Location: /new-endpoint
Content-Length: 0
A client that follows it repeats the request, same method included, at /new-endpoint. Depending on settings, an API client may hand you the 3xx instead of following it.
Redirect Status Codes
301and308indicate a permanent move;302and307are temporary.301and302permit a client to change POST to GET.307and308preserve the method.303tells the client to fetch a different resource with GET (or HEAD). The POST is not replayed.
These rules come from RFC 9110 §15.4. “Temporary” doesn’t mean uncacheable: a 302 is cached when its Cache-Control or Expires allows it. 301 and 308 are heuristically cacheable, so a browser may remember them even without explicit caching headers.
URL Formats
Location: /account
Location: ../account
Location: https://example.com/account
Relative values resolve against the URL that was requested. Watch out for values starting with //: that’s a protocol-relative URL, which keeps the scheme but names a host, so //evil.example/ sends the user to another site. A leading slash doesn’t make a value a local path.
Real-World Examples
To redirect a moved route in nginx and keep the method, put this inside a server block:
location = /old-endpoint {
return 308 /new-endpoint;
}
The nginx return directive accepts a local URI like this one and builds the full redirect URL from the request scheme and server configuration. Behind a proxy, check the Location nginx actually sends.
SEO Considerations
When a page moves for good, send 301 or 308 to its replacement. Google treats permanent redirects as a strong canonicalization signal, though the index updates on Google’s schedule, not instantly.
Map each old URL to its real replacement: /old-report should go to the new report, not to the homepage along with every other removed URL. Then check what the replacement returns; a correct first hop may lead into a loop.
Security Considerations
Copying a next query parameter straight into Location creates an open redirect that phishers can abuse. For a small, fixed set of destinations, use an allowlist:
app.get('/continue', (req, res) => {
const allowed = new Set(['/account', '/orders'])
const target = allowed.has(req.query.next) ? req.query.next : '/account'
res.redirect(303, target)
})
Express’s res.redirect() sends whatever target you give it; validating it is your job. The allowlist here rejects absolute URLs, protocol-relative URLs and any path not on the list.
Getting Location right
On a 201, include Location whenever the new resource has its own URL. It isn’t mandatory: without it, RFC 9110 §15.3.2 says the request’s target URI identifies the created resource.
On redirects, set the status you mean explicitly, then check the header that comes out. A 3xx without Location gives the client no target, and browsers handle that differently, so there’s no single error message to look for.
Creation headers, with the JSON body left out:
HTTP/1.1 201 Created
Location: /reports/42
Content-Type: application/json
The client can store /reports/42 and fetch it later; the 201 doesn’t ask it to go there now. A 303 after a form submission is different: it does ask the client to go fetch that URL.
Common Patterns
After a successful form POST, answer with 303 to send the browser to a GET confirmation page (the POST/redirect/GET pattern). The redirect won’t resend the POST, but users can double-submit, so keep server-side duplicate protection.
Check each hop with plain GET requests. Leave out -X POST when you test how redirects change methods: curl applies an explicit -X method to every request it follows, which hides the change you’re testing:
curl -sS -D - -o /dev/null https://example.com/old-endpoint
curl -L -v -o /dev/null https://example.com/old-endpoint
The first command shows only the first response; -L follows the redirects.
In Express, once the form’s operation has completed, end the handler with:
res.redirect(303, '/confirmation')
Send it only after the write has committed, not while async persistence is still running. If the next endpoint needs the original method and body, use 307 or 308 instead of POST/redirect/GET.
Related Headers
Frequently asked questions
What is the Location header?
Location specifies the URL to redirect to. It is used with 3xx redirect responses and 201 Created to indicate where the new resource was created.
Should Location be absolute or relative?
Both absolute and relative URI references are valid. A client resolves a relative Location value against the request target URI.
When is Location used with 201?
With 201 Created, Location points to the newly created resource. This lets clients know where to find the resource they just created via POST.
What happens if Location is missing from a redirect?
Then the client has no redirect target, and clients differ in how they handle that. Not every 3xx is a redirect anyway: 304 Not Modified, for example, never carries one.
Sources
Related
Refresh Header
Learn how the Refresh header instructs browsers to reload or redirect after a delay. Understand its use cases, limitations, and better alternatives.
Content-Disposition Header
Learn how the Content-Disposition header controls whether content displays inline or downloads as an attachment. Set custom filenames for file downloads.
Content-Language Header
Learn how the Content-Language header specifies the natural language(s) of response content. Understand language tags and internationalization best practices.
Content-Length
Learn how Content-Length specifies the body size in bytes. Essential for progress indicators, connection management, and chunked transfer decisions.