Quick verdict: cURL stops at the first 3xx response by default. Add -L (or --location) to follow redirects. Three gotchas: (1) POST changes to GET on 301/302/303 redirects unless you use --post301 / --post302 / --post303; (2) Authorization headers strip on cross-host redirects unless you set --location-trusted (do NOT do this on untrusted destinations); (3) the default redirect cap is 50, control with --max-redirs N.
$ curl https://bit.ly/some-link
<html>
<head><title>301 Moved Permanently</title></head>
<body><a href="https://final-destination.com">moved</a></body>
</html>cURL got the 301 response and stopped — printing the body the server returned (often a tiny HTML page) and exiting with status 0.
$ curl -L https://bit.ly/some-link
# now gets the real page from final-destination.comcURL follows up to 50 redirects by default. With -v you can see each hop:
curl -v -L https://bit.ly/some-link 2>&1 | grep -E "> (GET|Host)|< HTTP|< Location"That shows each request line, response code, and Location header in the chain.
curl -L --max-redirs 5 https://example.com/rBail out if the chain exceeds 5 redirects (defends against malicious redirect loops). Default is 50. Set --max-redirs 0 to disable following entirely (same as omitting -L).
Setting --max-redirs -1 means unlimited — do not use this on untrusted URLs.
Per RFC 7231, when a server returns 301, 302, or 303 to a POST, the redirected request should be a GET. cURL respects this. So:
# Submit a form, server 302s to /thanks
curl -L -X POST https://example.com/submit -d "name=alice"
# cURL POSTs to /submit, gets 302, then GETs /thanks (body discarded)If you actually want the POST to retry as POST after the redirect:
curl -L --post301 -X POST https://example.com/submit -d "name=alice"
# or --post302, --post303 depending on the response codeFor 307 and 308, cURL preserves the method automatically — that is the whole point of those status codes (RFC 7538).
By default, cURL strips the Authorization header on redirects to a different host:
$ curl -L -H "Authorization: Bearer SECRET" https://api.example.com/data
# If /data 302s to https://attacker.com, cURL does NOT forward the Bearer header.This is a security feature. Without it, a malicious redirect could exfiltrate your token to a third-party host.
To explicitly trust the redirect chain (use sparingly):
curl -L --location-trusted -H "Authorization: Bearer SECRET" https://api.example.com/dataOnly use --location-trusted when you trust EVERY URL in the chain. For commercial APIs that legitimately redirect across subdomains, use --location-trusted — but never on user-supplied URLs.
-w with %{redirect_url} shows where cURL would go next without actually following:
curl -s -o /dev/null -w "Redirect: %{redirect_url}\nStatus: %{http_code}\n" https://bit.ly/linkTo see EVERY hop's URL with -L:
curl -sIL https://bit.ly/link | grep -i "^Location"-I uses HEAD requests (so cURL does not download bodies), -L follows, and the Location headers print as the chain unfolds.
If a redirect goes HTTP → HTTPS or vice-versa, cURL handles it transparently. But if either hop fails SSL verification, the whole chain fails. The fix is the same as for any SSL issue: --cacert for self-signed targets, NOT -k (see why -k is dangerous).
cURL follows cookies across redirects when you pass -b and -c:
curl -L -b cookies.txt -c cookies.txt https://example.com/login-flowThe server sets a session cookie on /login, redirects to /dashboard, cURL forwards the cookie. Without the cookie jar, the destination would not know you logged in.
Proxy is set once and used for every hop:
curl -L --proxy http://USER:[email protected]:8000 https://bit.ly/shortEvery hop in the redirect chain routes through the proxy. Useful when the final destination is geo-locked or rate-limited.
--max-redirs 100 only if you trust the chain.--location-trusted if appropriate, OR have the API team fix the redirect to stay on the same host.--post301/--post302/--post303, or have the API use 307/308 instead.-L for downloads — CDN endpoints almost always redirect to the actual file URL.--max-redirs at a sensible value (5-10) for user-facing scripts.--location-trusted on URLs from untrusted sources (user input, third-party APIs).curl -sIL URL | grep -i location shows the full chain without downloading bodies.Related: cURL GET, cURL download files, cURL auth.