All posts

curl Follow Redirects: Test Every Hop With -L, -I and -w

7 min read

To make curl follow redirects, run curl -sIL <url>: it follows every hop in a redirect chain and prints each response’s headers. Add -w "%{url_effective} " -o /dev/null if you only want the final destination, or drop -L entirely with -I for a single-hop headers check. Cap the chase with --max-redirs N so a misconfigured loop doesn’t hang your terminal, and reach for -v or --trace-ascii when you need to see exactly what each server sent back.


TL;DR:

  • curl does not follow redirects by default; you need to specify -L or --location to chase a redirect chain effectively.
  • The behavior of curl when following redirects depends on the HTTP status code, with specific flags like --post301 or --post302 preserving POST methods across redirects.
  • Understanding Location and server headers helps identify redirect targets, but HEAD requests (-I) can behave differently from GET requests, especially with content-based redirects.
  • Verbose (-v) mode and trace options (--trace-ascii) are essential for debugging redirect loops and understanding exactly what servers send back at each hop.
  • When automating bulk redirect checks, use scripting with --max-redirs and timeouts to prevent stalls, and compare outputs over time to detect regressions.

Table of Contents

Quick Cheat Sheet for Testing Redirects With curl

curl does not follow redirects unless you tell it to. By default, a 301 or 302 response just lands on your terminal as a 3xx status line with a Location header, full stop. That surprises a lot of people coming from a browser, where redirects happen invisibly. The Everything confirms this directly: you need -L or --location to make curl chase the chain, and curl will follow up to 30 hops before giving up unless you change that with --max-redirs.

Here are the commands worth bookmarking:

  • Follow the full chain and see every header: curl -sIL https://example.com — the -s keeps the output quiet, -I fetches headers only, and -L follows each redirect until it hits a 200 or runs out of hops.
  • Print just the final URL: curl -Ls -w "%{url_effective} " -o /dev/null https://example.com — this discards the response body and prints only where you actually landed, which is the fastest way to confirm a redirect chain resolves correctly per the Unix Stack Exchange write-up on redirect targets.
  • Check a single hop without following: curl -I https://example.com — useful when you specifically want to see the first redirect response, not the destination.
  • Limit hops and catch loops: curl -L --max-redirs 5 https://example.com — if the chain exceeds five redirects, curl exits with an error instead of spinning forever.

Each of these is a complete, runnable one-liner. Copy one, swap in your URL, and you’ll have an answer in under a second.

What Do 3xx Status Codes and Redirect Headers Actually Mean?

The status code tells you what kind of redirect you’re dealing with, and that distinction changes how clients (including curl) behave. A 301 (Moved Permanently) and a 308 (Permanent Redirect) both signal a lasting change, but 308 explicitly preserves the original HTTP method while 301 leaves that ambiguous, which is exactly why browsers and curl historically converted POST to GET on a 301. A 302 and 307 follow the same split: 302 is the older, method-ambiguous “temporary” code, and 307 is its strict, method-preserving successor. A 303 (See Other) explicitly tells the client to switch to GET regardless of the original method, which is the standard pattern after a form submission.

The Location header carries the actual redirect target. That’s the header you’re scanning for in any -I or -v output. Beyond Location, keep an eye on Server and any nonstandard header like X-Redirect-By, which some CMS plugins and CDN edge rules add specifically to flag which layer issued the redirect. Neither is guaranteed to exist, but when it does, it saves you a lot of guessing.

One catch: -I sends a HEAD request, and some servers respond differently to HEAD than they do to GET, especially with application-level redirects triggered by content logic rather than server config. If your headers-only check doesn’t match what a browser shows, rerun with -v on a full GET.

How curl Handles HTTP Methods Across Redirects

This is where a lot of API testing goes wrong. curl’s default behavior, when following a redirect from a POST request, is to switch the follow-up request to GET on certain status codes. That’s fine for a form-to-confirmation-page pattern, but it silently breaks any test where you expect the server to receive a second POST, such as a webhook retry or a payment callback.

The curl manpage documents the flags built specifically to control this:

  1. --post301 forces curl to keep the request as POST when following a 301, overriding the default downgrade-to-GET behavior.
  2. --post302 does the same for 302 responses, which is the code most legacy frameworks still use for “temporary” redirects.
  3. --post303 keeps POST intact across a 303 response, even though 303 is designed to signal a method switch, giving you a way to test how a server-side integration behaves if a client ignores that signal.
  4. --request lets you override the HTTP method entirely at any point, which is handy for reproducing exactly what a third-party webhook sender would do.

If you’re testing an API integration that redirects, run the same check twice: once with default behavior, once with the matching --postXXX flag. The difference tells you whether your server depends on curl’s default downgrade or actually expects the method preserved.

Tracing Redirect Loops and Debugging Where They Come From

Verbose mode is where curl stops being a black box. Running -v prints every request line (marked with >) and every response line (marked with <), so you can watch the exact sequence of redirects unfold hop by hop, including headers you’d never see in a plain -L run. For a full byte-level record you can save and review later, --trace-ascii output.txt dumps the entire request and response exchange to a file.

A few patterns worth knowing:

  • If you see “Maximum redirects followed” as an error, that’s curl hitting the --max-redirs ceiling, not a fluke, and it usually means a real loop exists somewhere in the chain.
  • Community examples on Unix Stack Exchange show combining -sIL with grep -i location to pull just the redirect targets out of a noisy verbose dump.
  • Swapping the User-Agent with -A "Googlebot/2.1" can expose redirects that only trigger for specific bots or device classes, a pattern documented in guides on tracing redirects with curl.
  • When curl’s output doesn’t explain the redirect, pair it with dig to rule out a DNS-level rewrite, or openssl s_client to check whether TLS termination at a load balancer is the actual source.

Scripting and Bulk Redirect Checks

Testing one URL by hand is fine until you have a spreadsheet of 200 old blog links after a migration. At that point you want a loop, not a memorized command.

  1. Build the loop. Read URLs from a file with while read -r url; do curl -s -o /dev/null -w "%{http_code} %{num_redirects} %{url_effective} " "$url"; done < urls.txt. That single -w format string gives you the status code, how many redirects fired, and where you ended up, all in one line per URL.
  2. Snapshot the output. Redirect that loop’s output into a dated file (redirects_2026_02_10.txt) and diff it against a previous run. Any line that changes status code or final URL is a regression worth investigating before a client notices.
  3. Guard against runaway automation. Add --max-redirs 10 and --max-time 15 to every scripted call. Without a timeout, one unresponsive server can stall an entire batch job, and without a redirect cap, one loop can burn through your rate limits on a target you don’t control.

What Actually Slows Down Redirect Debugging

Most redirect mysteries I’ve seen traced back to one of three places: a CDN edge rule, a server config block, or a CMS plugin firing its own logic on top of whatever the origin server intended. curl won’t tell you which one it is directly, but the Server header and any X-Redirect-By string usually narrow it down fast, since plenty of stacks tag their own redirects that way specifically so debugging doesn’t turn into archaeology.

The real split in tooling is server-side versus client-side. curl only ever sees what the server sends back, so if a redirect is actually happening through JavaScript in the browser after the page loads, curl will show you a clean 200 and nothing else looks wrong. That’s when you switch to an actual browser with dev tools open, not another curl flag.

Pro Tip: Run --trace-ascii trace.txt once during initial debugging and keep the file. Redirect issues have a habit of reappearing weeks later, and having the original byte-level trace saved beats trying to reproduce the exact conditions again from memory.

— Andrea

Every command in this guide works the same way against any short link, which makes rdyrct a genuinely useful sandbox if you want to see redirect chains in action without standing up your own test server. rdyrct turns a long URL into a short, branded link, and running curl -sIL against one shows you the exact 3xx response and Location header the platform generates.

Because some URL shorteners do not store IP addresses, the analytics you get back can be limited to referrer, device, and country, which matters if you’re testing redirect behavior on links tied to a real campaign and don’t want click data doubling as a privacy liability. The free plan covers enough links to run your own header and status-code tests today, and if you need branded QR codes or custom domains layered on top, the pricing page breaks down what unlocks at each tier. Create a link, point curl at it, and check the response yourself.

Sources

Keep reading. More from the blog.

  1. rdyrct 1.1: bring your links from Bitly, then tag, shorten and track them

    rdyrct 1.1.0 is out: import your links from Bitly, Short.io, Rebrandly or Dub with the same short URLs, a free UTM builder, links made from the homepage, and your own copy in one step.

  2. Best URL Shortener in 2026: the Buyer's Shortlist

    The best URL shorteners in 2026, ranked on price, custom domains, link limits and analytics history, with the plan numbers from each vendor's pricing page.

  3. Shorter links on rdyr.cc

    New short links on rdyrct's shared domain now use rdyr.cc, a shorter domain that only redirects. Every rdyrct.com link keeps working, on both domains.

Short links on your own domain. Free to start.

Links, QR codes and click analytics for your team. No visitor IP address is ever stored.