Canonical hostnames, HTTPS upgrades, and API gateways often answer with redirect statuses instead of the final payload. Following those responses correctly keeps a single cURL command aligned with the real endpoint and avoids brittle wrapper logic that parses Location headers manually.
By default, cURL stops at the first 3xx response and prints that response as-is. Adding --location (or -L) makes cURL read the Location header, request the next URL, and continue until a non-redirect response arrives or the redirect limit is reached. Pairing redirect following with --include, --write-out, or --verbose makes each hop and the final destination visible.
Redirect chains should stay bounded and explicit. By default, cURL does not forward credentials or explicit Cookie: headers to a different host during a redirect, while --location-trusted does and should be reserved for trusted targets only. Redirected POST requests also switch to GET on 301, 302, and 303 unless the matching --post301, --post302, or --post303 option is added. The hostnames and paths in the examples below stay masked while preserving a realistic edge-to-API redirect flow.
Steps to follow HTTP redirects with cURL:
- Request the URL once without redirect following to confirm the returned status and Location header.
$ curl --silent --show-error --include "https://edge.example.test/redirects/2" HTTP/2 302 location: /routing/us-east/1 cache-control: no-store content-length: 0 ##### snipped #####
A visible 302 plus a Location header confirms that the server expects another request instead of returning the final resource immediately.
- Re-run the same URL with --location so cURL follows the full chain and stops on the final response.
$ curl --silent --show-error --location --include "https://edge.example.test/redirects/2" HTTP/2 302 location: /routing/us-east/1 ##### snipped ##### HTTP/2 302 location: https://api.example.test/v1/health ##### snipped ##### HTTP/2 200 content-type: application/json; charset=utf-8 ##### snipped #####
The last status block should be the non-redirect response that the command actually needs, not another 3xx hop.
- Capture only the final destination, redirect count, and response status when the body is not needed.
$ curl --silent --show-error --location --output /dev/null --write-out "Final URL: %{url_effective}\nRedirects followed: %{num_redirects}\nHTTP status: %{http_code}\n" "https://edge.example.test/redirects/2" Final URL: https://api.example.test/v1/health Redirects followed: 2 HTTP status: 200These write-out values are useful in health checks and CI probes because they expose redirect drift without saving the response body.
- Bound the chain with --max-redirs before using redirect following in scripts and scheduled jobs.
$ curl --silent --show-error --location --max-redirs 1 "https://edge.example.test/redirects/3" curl: (47) Maximum (1) redirects followed
The upstream curl default is 50 redirects, but an explicit project limit makes loops and routing mistakes fail sooner and more predictably.
- Limit redirect targets to approved protocols with --proto-redir when the final destination must stay on HTTP or HTTPS.
$ curl --silent --show-error --location --proto-redir '=http,https' --output /dev/null --write-out "Final URL: %{url_effective}\nRedirects followed: %{num_redirects}\nHTTP status: %{http_code}\n" "https://edge.example.test/redirects/2" Final URL: https://api.example.test/v1/health Redirects followed: 2 HTTP status: 200Protocol restrictions reduce the risk of a changed redirect chain jumping to another allowed scheme such as FTP.
- Inspect each hop with --verbose when headers, host changes, or TLS behavior need confirmation.
$ curl --silent --show-error --verbose --location --output /dev/null "https://edge.example.test/redirects/1" * Host edge.example.test:443 was resolved. > GET /redirects/1 HTTP/2 < HTTP/2 302 < location: https://api.example.test/v1/health * Issue another request to this URL: 'https://api.example.test/v1/health' > GET /v1/health HTTP/2 < HTTP/2 200 ##### snipped #####
Verbose output should show both the original request line and the follow-up request that reaches the final URL.
- Preserve a redirected POST only when the redirect target is expected to keep the original request body.
$ curl --silent --show-error --location --post302 \ --data "mode=validate&profile=nightly" \ "https://edge.example.test/redirect-to?url=https://api.example.test/v1/jobs/validate&status_code=302" { "method": "POST", "url": "https://api.example.test/v1/jobs/validate", "data": "mode=validate&profile=nightly", ##### snipped ##### }Without --post302, the same 302 redirect changes the follow-up request to GET and drops the request body. Use --post301 or --post303 for the matching status codes when the redirect target requires the original POST semantics.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
