curl Follow Redirects: -L Flag, Options & Common Gotchas (2026)
Last updated: 2026 ยท ~1,900 words ยท 9 min read
โก Key Takeaways
- By default, curl stops at the first 3xx response and prints the redirect body without following it.
- Add
-L(or--location) to follow redirects automatically โ this is the only flag most use cases need. - curl follows up to 50 redirects by default; control this with
--max-redirs N. - POST becomes GET on 301/302/303 redirects โ use
--post301,--post302,--post303to preserve the method. - Auth headers are stripped on cross-host redirects โ use
--location-trustedonly on trusted destinations. - For scraping and automation, combining
-Lwith a proxy routes the entire redirect chain through the same exit IP.
curl is deliberately conservative about redirects. When a server returns a 3xx response, curl stops, prints whatever body the redirect response contains, and exits โ it does not automatically follow the Location header to the new URL. For browser users this would be maddening, but for a command-line tool used in scripts, it is the safer default: scripts should explicitly declare their intent rather than silently following chains of unknown length to unknown destinations.
One flag changes that behaviour entirely. This guide covers -L in full โ what it does at the protocol level, every 3xx redirect code and how curl handles each one, the three documented gotchas that trip up experienced developers, and a complete reference of every redirect-related flag with practical examples for common use cases.
Default curl Behaviour Without -L
Without any redirect flags, curl fetches the response from the URL you specify and stops. If that response is a 3xx redirect, curl prints the redirect body (usually a small HTML page or an empty body) and exits with status code 0:
# Without -L: curl stops at the redirect
$ 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 received the 301 response, printed its body, and exited. It did not request https://final-destination.com. This is not an error โ curl exit code 0 means the HTTP transaction completed successfully. The 3xx is a successful HTTP response; curl just is not following its instruction.
Following Redirects with -L
Add -L (long form: --location) to tell curl to follow the Location header whenever it receives a 3xx response:
# -L follows redirects to the final destination $ curl -L https://bit.ly/some-link <!DOCTYPE html>... โ content from the final destination URL # Equivalent long form: $ curl --location https://bit.ly/some-link
With -L, curl resends the request to whatever URL appears in the Location header, repeating this process until it reaches a non-3xx response or hits the redirect limit. As SpyderProxy's 2026 curl guide describes it: curl becomes "a redirect-following powerhouse" โ the -L flag is the only addition most use cases require.
3xx Redirect Codes: How curl Handles Each One
Not all redirects behave identically. The most important difference is whether curl changes the HTTP method (from POST to GET) on the redirected request:
301
Resource permanently moved to new URL. curl follows with -L.
302
Temporary redirect. curl follows with -L.
303
Explicitly instructs client to GET the new resource. curl follows.
POST โ GET (by design)307
Temporary redirect that preserves the original method and body.
Method preserved โ308
Permanent redirect with method preservation. The modern replacement for 301.
Method preserved โ300
Server offers multiple options. curl follows if a Location header is present.
Method-change behaviour per ReqBin curl redirect reference; 307/308 semantics from MDN Web Docs HTTP 307.
The method-change behaviour of 301/302/303 is intentional per the HTTP specification, but it regularly surprises developers sending POST requests through redirect chains. If your API uses 301 or 302 redirects and your client is posting data, the data will be silently dropped on the redirect.
3 Gotchas Every Developer Hits With curl Redirects
โ ๏ธ Gotcha 1: POST Becomes GET
When curl follows a 301, 302, or 303, it changes the method to GET โ dropping the original POST body. This is standard HTTP behaviour, but it silently discards your form data or JSON payload mid-chain. Fix: use --post301, --post302, --post303 flags, or have your API use 307/308 redirects instead.
โ ๏ธ Gotcha 2: Auth Headers Stripped Cross-Host
If a redirect leads to a different hostname, curl strips Authorization headers and credentials from the follow-up request โ by design, to prevent credential leakage to unknown destinations. Fix: use --location-trusted only when you control and trust the entire redirect chain. Never use on user-supplied URLs.
โ ๏ธ Gotcha 3: Redirect Loops Hit the Cap
A misconfigured server that redirects AโBโA produces the error: curl: (47) Maximum (50) redirects followed. This almost always indicates a server-side loop, not a too-low limit. Fix: use -v to inspect the chain, then fix the loop rather than raising --max-redirs blindly.
Full Flag Reference: curl Redirect Options
| Flag | Long Form | What It Does | When to Use |
|---|---|---|---|
-L |
--location |
Follow 3xx redirects automatically | Almost always โ the base flag every redirect use case needs |
--max-redirs N |
--max-redirs |
Set max redirects to follow (default: 50; -1 = unlimited) | Scripts where you want a safety cap; set 5โ10 for most use cases |
--post301 |
--post301 |
Keep POST method (don't switch to GET) on 301 redirects | Submitting forms or JSON payloads through 301-redirecting endpoints |
--post302 |
--post302 |
Keep POST method on 302 redirects | Same as above for 302 |
--post303 |
--post303 |
Keep POST method on 303 redirects | Rarely needed โ 303 explicitly asks clients to GET; only override if server is misconfigured |
--location-trusted |
--location-trusted |
Send credentials to all redirect destinations, including cross-host | Only on fully trusted internal redirect chains. Never on user-supplied URLs. |
-v |
--verbose |
Show full request/response headers at each redirect hop | Debugging redirect chains โ see every Location header in sequence |
-I |
--head |
Fetch headers only (no body) at the final redirect destination | Checking redirect target without downloading content |
-w "%{url_effective}" |
--write-out |
Print the final URL after following all redirects | Resolving shortened URLs or verifying redirect destinations in scripts |
-c cookie.txt |
--cookie-jar |
Save cookies received across the redirect chain | Authenticated redirects where session cookies are set early in the chain |
-b cookie.txt |
--cookie |
Send saved cookies on each request in the redirect chain | Maintaining session state across redirects that require cookies |
Practical Examples: Every Common Redirect Scenario
Basic redirect following
# Follow redirects to the final destination curl -L https://example.com/old-path
Limit redirect hops
# Follow at most 5 redirects โ good for production scripts curl -L --max-redirs 5 https://example.com/redirect-chain
Inspect the full redirect chain
# See every Location header without downloading response bodies curl -sIL https://bit.ly/some-link | grep -i "location\|http/" # Full verbose โ every request and response header at each hop curl -vL https://example.com/redirect 2>&1 | grep -E "^[<>] |HTTP/"
Find the final URL after all redirects
# Print only the final resolved URL โ useful for URL shortener resolution curl -Ls -o /dev/null -w "%{url_effective}\n" https://bit.ly/some-link https://final-destination.com/actual/path
Keep POST through 301/302 redirects
# POST data will NOT be dropped on the 301 redirect curl -L --post301 --post302 \ -H "Content-Type: application/json" \ -d '{"key": "value"}' \ https://api.example.com/old-endpoint
Download a file through a CDN redirect
# CDN endpoints almost always redirect to the actual file location # -L is mandatory; -o saves to a file; -C - resumes partial downloads curl -L -C - -o output.zip https://releases.example.com/latest.zip
Follow redirects with authentication
# Credentials sent only to the original host (secure default) curl -L -u user:pass https://api.example.com/protected # WARNING: --location-trusted forwards credentials to ALL redirect targets # Only use on fully trusted internal redirect chains curl -L --location-trusted -u user:pass https://internal.example.com/auth-redirect
Maintain cookie sessions across redirects
# Save cookies from the redirect chain to a file curl -L -c cookies.txt https://example.com/login-redirect # Send those cookies on the next request curl -L -b cookies.txt https://example.com/dashboard
Follow redirects through a proxy
# The entire redirect chain routes through the proxy's IP # Essential for scraping: all hops use the same exit IP curl -L -x http://user:pass@proxy.nstproxy.io:8080 https://example.com/redirect
3 Real-World Scenarios
Downloading Software Releases
GitHub and most CDN-hosted downloads use a two-hop redirect: the release URL redirects to a CDN-signed URL that expires after a few minutes. Without -L, curl downloads the HTML redirect page instead of the file. With -L -C -, curl follows the redirect and resumes interrupted downloads โ critical for large files on slow connections.
Resolving URL Shorteners in Scripts
A monitoring script needs to resolve bit.ly or t.co URLs to verify they still point to valid destinations. Using curl -Ls -o /dev/null -w "%{url_effective}\n" resolves the full chain and prints the final URL without downloading any content โ a clean one-liner for batch URL resolution with no HTML noise in the output.
Web Scraping Through Redirect Chains
E-commerce product pages frequently redirect: http:// โ https:// โ canonical URL โ localised URL. A scraper without -L only sees the first redirect body. More importantly, routing redirects through a proxy with -L -x proxy:port ensures all hops in the chain use the same residential IP โ preventing the target from seeing a series of requests from different IPs that would trigger bot detection.
Debugging Redirect Chains Methodically
When a redirect chain behaves unexpectedly, a structured debugging approach identifies the failure point faster than trial and error.
# Step 1: See the full chain โ all Location headers, no bodies curl -sIL https://example.com/redirect | grep -i "http\|location" # Step 2: Full verbose โ every request/response pair curl -vL https://example.com/redirect 2>&1 | less # Step 3: Confirm final destination curl -Ls -o /dev/null -w "Status: %{http_code}\nFinal URL: %{url_effective}\nTotal redirects: %{num_redirects}\n" \ https://example.com/redirect # Step 4: Test without credentials to isolate auth issues curl -vL https://example.com/redirect 2>&1 | grep -i "authorization\|location\|http/" # Step 5: Check if proxy is affecting the chain curl -vL -x http://proxy:port https://example.com/redirect 2>&1 | grep -i "location\|http/"
curl Redirect Flags in Python and Other HTTP Libraries
The concepts map directly to every major HTTP library โ understanding curl's flags helps when configuring redirect behaviour in code:
# Python requests โ follows redirects by default (opposite of curl) import requests # Disable redirect following (curl's default behaviour) resp = requests.get("https://example.com/redirect", allow_redirects=False) # Follow redirects with a proxy (equivalent to curl -L -x proxy:port) proxies = { "http": "http://user:pass@proxy.nstproxy.io:8080", "https": "http://user:pass@proxy.nstproxy.io:8080", } resp = requests.get("https://example.com/redirect", allow_redirects=True, proxies=proxies) print(resp.url) # Final URL after all redirects
Note the key difference: Python's requests library follows redirects by default (unlike curl). allow_redirects=False matches curl's no--L behaviour; allow_redirects=True (the default) matches curl with -L.
curl Redirect Flags for Web Scraping with Proxies
For web scraping pipelines, the proxy integration with redirect following has one non-obvious implication: with -L -x proxy:port, every hop in the redirect chain routes through the proxy. This is the correct behaviour for scraping โ you want all requests in the chain to appear as coming from the same residential IP, not for the final hop to originate directly from your machine.
Without a proxy, the final destination after following a cross-domain redirect sees your real IP โ potentially flagging the session as a bot if the originating IP was a residential proxy but the final request came from a datacenter. With -L -x proxy:port both hops go through the same exit IP, maintaining consistent session identity throughout the chain.
Nstproxy's residential proxies support this pattern natively โ HTTP and SOCKS5 endpoints that route all traffic including redirect chains through clean residential IPs. See the residential proxy overview and the proxy server tools guide for curl integration patterns.
Route curl Redirect Chains Through Clean Residential IPs
Nstproxy's 110M+ residential proxies integrate with curl via -x proxy:port โ every redirect hop routes through the same clean exit IP, maintaining session consistency across multi-hop chains.
Conclusion
curl's default behaviour of stopping at 3xx responses is a deliberate safety feature, not a limitation. Add -L to follow redirects; add --max-redirs 5 to cap the chain in production scripts; use -v to see every hop when debugging; use -w "%{url_effective}" to print the final URL without noise. The three gotchas โ POST-to-GET conversion, cross-host credential stripping, and redirect loops โ all have explicit flags that address them once you know they exist.
For scraping pipelines, the proxy integration is the most important operational detail: -L -x proxy:port routes the entire chain through the proxy's exit IP, maintaining consistent session identity across every hop. Without this, a multi-step redirect chain can expose your real IP at the final destination even when the initial request appeared to come from a residential proxy.
Frequently Asked Questions
Add the -L flag (or its long form --location) to your curl command: curl -L https://example.com/redirect. This tells curl to follow the Location header whenever it receives a 3xx response. Without -L, curl stops at the first redirect and prints the redirect response body instead of the final content.
curl follows up to 50 redirects by default when using -L. You can change this limit with --max-redirs N โ for example, --max-redirs 5 to cap at 5 hops. Setting --max-redirs -1 allows unlimited redirects (use cautiously). If you hit the limit, you see the error: curl: (47) Maximum (50) redirects followed โ usually indicating a redirect loop.
When curl follows a 301, 302, or 303 redirect, it changes the method to GET and drops the request body โ this is standard HTTP behaviour per the specification. To preserve POST across these redirects, add the --post301, --post302, or --post303 flags. Alternatively, if you control the server, use 307 or 308 redirects instead โ these explicitly preserve the original method and body.
Use curl -sIL https://example.com | grep -i "http\|location" to see every redirect hop's status code and Location header without downloading bodies. For full verbose output including all request and response headers at each hop, use curl -vL https://example.com 2>&1 | less. To print only the final resolved URL after all redirects: curl -Ls -o /dev/null -w "%{url_effective}\n" https://example.com.
Yes. When you combine -L with -x proxy:port, curl routes all redirect hops through the proxy โ every request in the chain uses the proxy's exit IP, not your machine's real IP. This is important for web scraping: without routing the full chain through the proxy, a multi-step redirect can expose your real IP at the final destination even if the initial request appeared to come from a residential proxy.

