GET /redirect-chain
Redirects N times via 302 before returning a final 200. Each hop decrements the counter in the query string. Tests redirect-following limits, history tracking, and loop detection.
n
Number of hops before resolving. Range: 0–20. Default: 3.
build a request:
expect: A chain of 302 redirects terminating in a 200 OK. Following clients see the final body; non-following clients see the first 302.
# curl follows redirects with -L
curl -iL https://chaos.catastrophic.io/redirect-chain?n=3
# See each hop
curl -v https://chaos.catastrophic.io/redirect-chain?n=3 2>&1 | grep -E "^[<>]"
import urllib.request
# urllib follows redirects automatically (capped at ~10).
resp = urllib.request.urlopen("https://chaos.catastrophic.io/redirect-chain?n=3")
print(resp.status, resp.url) # final URL after redirects
// fetch follows redirects by default; pass { redirect: "manual" } to disable.
const res = await fetch("https://chaos.catastrophic.io/redirect-chain?n=3");
console.log(res.status, res.redirected, res.url);
package main
import (
"fmt"
"net/http"
)
func main() {
// Default client follows up to 10 redirects.
// CheckRedirect lets you cap or trace each hop.
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 3 {
return http.ErrUseLastResponse
}
return nil
},
}
resp, _ := client.Get("https://chaos.catastrophic.io/redirect-chain?n=3")
defer resp.Body.Close()
fmt.Println(resp.StatusCode, resp.Request.URL.String())
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
fn main() -> Result<(), Box> {
let client = reqwest::blocking::Client::builder()
.redirect(reqwest::redirect::Policy::limited(3))
.build()?;
let resp = client
.get("https://chaos.catastrophic.io/redirect-chain?n=3")
.send()?;
println!("{} {}", resp.status(), resp.url());
Ok(())
}
// Java 11+ HttpClient. Default policy is NEVER — opt in explicitly.
import java.net.URI;
import java.net.http.*;
public class RedirectChain {
public static void main(String[] args) throws Exception {
var client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
var req = HttpRequest.newBuilder(
URI.create("https://chaos.catastrophic.io/redirect-chain?n=3")
).build();
var resp = client.send(req, HttpResponse.BodyHandlers.discarding());
System.out.println(resp.statusCode() + " " + resp.uri());
}
}
// .NET 6+
var handler = new HttpClientHandler {
AllowAutoRedirect = true,
MaxAutomaticRedirections = 10,
};
using var client = new HttpClient(handler);
var resp = await client.GetAsync("https://chaos.catastrophic.io/redirect-chain?n=3");
Console.WriteLine($"{(int)resp.StatusCode} {resp.RequestMessage?.RequestUri}");
require "net/http"
# Net::HTTP doesn't follow redirects automatically — loop manually.
uri = URI("https://chaos.catastrophic.io/redirect-chain?n=3")
limit = 10
loop do
res = Net::HTTP.get_response(uri)
break puts("#{res.code} #{uri}") unless res.is_a?(Net::HTTPRedirection)
raise "Too many redirects" if (limit -= 1) <= 0
uri = URI(res["location"])
end
# Invoke-WebRequest follows redirects automatically.
# Use -MaximumRedirection to test the limit.
Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/redirect-chain?n=3' `
-MaximumRedirection 3 # Will throw if chain exceeds 3 hops
headers
body