Skip to content

/commands/http/follow-redirects-curl

curl follow redirects and show the redirect chain (curl -L)

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · curl 8.21.0 (x86_64-w64-mingw32) libcurl/8.21.0 Schannel zlib/1.3.2updated
tool
HTTP
versions
curl 7.x / 8.x
os
linux, macos, windows

curl -L URL follows HTTP redirects: when the response is a 3xx with a Location header, curl sends a new request to that location and repeats until it gets a non-redirect response. To see every hop, combine it with -I (headers only) or -i, and filter the status lines and Location headers. curl follows at most 50 redirects by default (--max-redirs).

See the redirect chain

bash
cat > server.js <<'EOF'
const http = require("http");
http.createServer((req, res) => {
  if (req.url === "/old") { res.writeHead(301, { Location: "/new" }); return res.end(); }
  if (req.url === "/new") { res.writeHead(302, { Location: "/final" }); return res.end(); }
  res.end("final page\n");
}).listen(5206, "127.0.0.1");
setTimeout(() => process.exit(0), 10000);
EOF
node server.js &
sleep 1
echo "== without -L"
curl -s -i http://127.0.0.1:5206/old | tr -d '\r' | grep -iE '^(HTTP|location)'
echo "== with -L, every hop"
curl -s -I -L http://127.0.0.1:5206/old | tr -d '\r' | grep -iE '^(HTTP|location)'
echo "== summary"
curl -s -L -o /dev/null -w '%{http_code} %{num_redirects} %{url_effective}\n' http://127.0.0.1:5206/old
echo "== limit"
curl -s -S -L --max-redirs 1 http://127.0.0.1:5206/old; echo "exit status: $?"
wait

Output

== without -L
HTTP/1.1 301 Moved Permanently
Location: /new
== with -L, every hop
HTTP/1.1 301 Moved Permanently
Location: /new
HTTP/1.1 302 Found
Location: /final
HTTP/1.1 200 OK
== summary
200 2 http://127.0.0.1:5206/final
== limit
curl: (47) Maximum (1) redirects followed
exit status: 47

The summary line uses write-out variables: the final status, the number of redirects followed and the URL of the last request. When the limit is reached, curl stops with exit code 47 and the message "Maximum (1) redirects followed".

Options

OptionEffect
-L, --locationFollow 3xx responses that carry a Location header
--max-redirs NStop after N redirects (default 50; -1 means no limit)
-w '%{redirect_url}'Without -L: print where the redirect points, without following it
-w '%{num_redirects}'With -L: how many redirects were followed
-w '%{url_effective}'The URL of the last request made
--post301 / --post302 / --post303Keep POST instead of switching to GET on these codes
--location-trustedSend credentials to the redirected host too (security risk)

Which status codes redirect

RFC 9110 defines 301 (Moved Permanently), 302 (Found), 303 (See Other), 307 (Temporary Redirect) and 308 (Permanent Redirect). 307 and 308 require the client to repeat the same method and body; for 301 and 302 clients historically switch POST to GET, and 303 always means "fetch the result with GET". 304 Not Modified is a 3xx code but not a redirect: it has no Location and curl does not follow it.

Pitfalls

  • A loop (/a redirects to /b, /b back to /a) ends with exit code 47 after 50 hops. Use -I -L --max-redirs 5 to print the loop and fix the server rule.
  • -I sends HEAD. A server that redirects GET but answers HEAD differently shows a different chain; use -s -o /dev/null -D - -L to dump the headers of real GET requests instead.
  • An HTTP-to-HTTPS redirect followed by a certificate problem fails on the second hop, so the TLS error appears even though you typed an http:// URL.

FAQ

Why does curl print nothing for a URL that works in the browser?

The server probably answers with a redirect (301, 302, 307 or 308) and an empty body. curl does not follow redirects unless you add -L, so it prints that empty body. Add -i to see the status line and the Location header.

Does curl -L keep the POST method on a redirect?

For 307 and 308 it repeats the same method and body. For 301, 302 and 303 it switches to GET, as browsers do; --post301, --post302 and --post303 keep POST for those codes.

Does curl send my Authorization header to the redirected host?

Not to a different host. curl drops credentials and custom Authorization and Cookie headers when a redirect goes to another host, unless you add --location-trusted. Use that only for hosts you trust.

Sources