/commands/curl/curl-follow-redirects
curl -L: follow redirects (301, 302) with curl
- tool
- curl
- versions
- curl 7.x, curl 8.x
- os
- linux, macos, windows
curl -L URL follows HTTP redirects: when the server answers with a 3xx status and a Location header, curl requests the new URL and repeats until it gets a final response. Without -L, curl prints the body of the redirect response (often empty) and stops, which is why downloads from GitHub releases, short links and http:// to https:// upgrades seem to return nothing. curl follows at most 50 redirects by default; change it with --max-redirs N.
Example
A Node.js server on 127.0.0.1 redirects /old to /new with a 301, and /loop to itself:
cat > server.js <<'EOF'
const s = require("http").createServer((req, res) => {
if (req.url === "/old") { res.writeHead(301, { Location: "/new" }); return res.end(); }
if (req.url === "/loop") { res.writeHead(302, { Location: "/loop" }); return res.end(); }
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`final page, method ${req.method}\n`);
});
s.listen(5142, "127.0.0.1");
setTimeout(() => process.exit(0), 15000);
EOF
node server.js &
sleep 1
U=http://127.0.0.1:5142
echo "without -L:"; curl -s -w 'status %{http_code}, location %{redirect_url}\n' "$U/old"
echo "with -L:"; curl -sL "$U/old"
curl -sL -o /dev/null -w '%{url_effective} after %{num_redirects} redirect(s)\n' "$U/old"
echo "POST:"; curl -sL -d x=1 "$U/old"
curl -sL --post301 -d x=1 "$U/old"
curl -sSL --max-redirs 3 "$U/loop"; echo "exit $?"Output
without -L: status 301, location http://127.0.0.1:5142/new with -L: final page, method GET http://127.0.0.1:5142/new after 1 redirect(s) POST: final page, method GET final page, method POST curl: (47) Maximum (3) redirects followed exit 47
Without -L curl reports the 301 and the Location it did not follow. With -L it reaches /new. A POST became a GET after the 301 unless --post301 was given. The loop stopped after three redirects with exit code 47.
Options
| Option | Effect |
|---|---|
| -L, --location | Follow 3xx redirects |
| --max-redirs N | Stop after N redirects (default 50; -1 means no limit) |
| --post301 / --post302 / --post303 | Keep POST instead of switching to GET for that status |
| --location-trusted | Also send credentials and Authorization to other hosts (use with care) |
| -I -L | Headers of each step of the chain |
| -w '%{url_effective}' | Final URL |
| -w '%{num_redirects}' | Number of redirects followed |
| -w '%{redirect_url}' | Where the response would redirect to, when -L is not used |
Which status codes
| Status | Meaning | Method when curl follows |
|---|---|---|
| 301 Moved Permanently | Permanent move | POST becomes GET unless --post301 |
| 302 Found | Temporary move | POST becomes GET unless --post302 |
| 303 See Other | Look at another URL, usually after a POST | GET unless --post303 |
| 307 Temporary Redirect | Temporary, method kept | Same method and body |
| 308 Permanent Redirect | Permanent, method kept | Same method and body |
Errors
curl: (47) Maximum (N) redirects followed: a redirect loop or a very long chain. Look at it withcurl -sIL URL | grep -i '^location'; loops often come from an HTTP to HTTPS rule combined with a proxy that forwards plain HTTP.- Empty output and exit 0 without
-L: the response was a redirect with no body. Add-Lor check the status with-w '%{http_code}'. - Redirects to another scheme are limited: curl follows HTTP and HTTPS by default and refuses to switch to protocols such as
file://.
Platform notes
The same flags work on Linux, macOS and with curl.exe on Windows 10 and later. In Windows PowerShell 5.1 curl is an alias of Invoke-WebRequest, which follows redirects on its own and takes different parameters; type curl.exe.
FAQ
Does curl -L send my Authorization header to the new host?
No. Since curl 7.58 credentials and custom Authorization and Cookie headers are only sent to the host of the original URL. Add --location-trusted to send them to other hosts too, which is only safe when you trust every host in the chain.
Why does a POST turn into a GET after a redirect?
For 301, 302 and 303 responses curl switches to GET when following, as browsers do. Keep the method with --post301, --post302 or --post303, or have the server answer 307 or 308, which never change the method.
How do I see the final URL after redirects?
curl -sL -o /dev/null -w '%{url_effective}\n' URL prints it. %{num_redirects} gives the number of redirects followed.