GET /etag
Returns ETag and Last-Modified headers that break HTTP conditional-request semantics. Tests caches, CDNs, and clients that rely on validators.
mode
random-etag (default; different ETag every call for identical content), stable-etag-mutated-body (same ETag, body changes), weak-strong-conflict (both strong and weak ETag headers), unquoted (ETag without required quotes), future-last-modified (Last-Modified in 2099).
build a request:
expect: 200 OK with a Content-Type of application/json and an ETag (and sometimes Last-Modified) header that violates RFC 9110 in the chosen way. X-Chaos-Etag-Mode reflects the selection; click try-it twice to see how the mode behaves across calls.
# Two calls — observe how the ETag and body change across them
curl -is 'https://chaos.catastrophic.io/etag?mode=random-etag' | grep -iE '^(etag|last-modified|x-chaos-etag-mode):'
curl -is 'https://chaos.catastrophic.io/etag?mode=random-etag' | grep -iE '^(etag|last-modified|x-chaos-etag-mode):'
import urllib.request
for i in range(2):
resp = urllib.request.urlopen("https://chaos.catastrophic.io/etag?mode=random-etag")
etag = resp.headers["ETag"]
lm = resp.headers["Last-Modified"]
print(f"call {i+1}: ETag={etag!r} Last-Modified={lm!r}")
print(" body:", resp.read().decode()[:120])
for (let i = 0; i < 2; i++) {
const res = await fetch("https://chaos.catastrophic.io/etag?mode=random-etag");
console.log(`call ${i+1}:`, res.headers.get("etag"), res.headers.get("last-modified"));
console.log(" body:", (await res.text()).slice(0, 120));
}
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
for i := 1; i <= 2; i++ {
resp, _ := http.Get("https://chaos.catastrophic.io/etag?mode=random-etag")
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("call %d: ETag=%q Last-Modified=%q\n",
i, resp.Header.Get("ETag"), resp.Header.Get("Last-Modified"))
fmt.Printf(" body: %s\n", string(body)[:min(120, len(body))])
}
}
func min(a, b int) int { if a < b { return a }; return b }
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
fn main() -> Result<(), Box> {
let client = reqwest::blocking::Client::new();
for i in 1..=2 {
let resp = client.get("https://chaos.catastrophic.io/etag?mode=random-etag").send()?;
println!("call {i}: ETag={:?} Last-Modified={:?}",
resp.headers().get("etag"), resp.headers().get("last-modified"));
println!(" body: {}", &resp.text()?[..120.min(usize::MAX)]);
}
Ok(())
}
import java.net.URI;
import java.net.http.*;
public class EtagChaos {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://chaos.catastrophic.io/etag?mode=random-etag")).build();
for (int i = 1; i <= 2; i++) {
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.printf("call %d: ETag=%s Last-Modified=%s%n",
i,
resp.headers().firstValue("ETag").orElse(""),
resp.headers().firstValue("Last-Modified").orElse(""));
}
}
}
using var client = new HttpClient();
for (int i = 1; i <= 2; i++)
{
var resp = await client.GetAsync("https://chaos.catastrophic.io/etag?mode=random-etag");
var etag = resp.Headers.ETag?.Tag ?? "";
var lm = resp.Content.Headers.LastModified?.ToString("R") ?? "";
Console.WriteLine($"call {i}: ETag={etag} Last-Modified={lm}");
}
require "net/http"
uri = URI("https://chaos.catastrophic.io/etag?mode=random-etag")
2.times do |i|
res = Net::HTTP.get_response(uri)
puts "call #{i+1}: ETag=#{res['ETag'].inspect} Last-Modified=#{res['Last-Modified'].inspect}"
end
1..2 | ForEach-Object {
$r = Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/etag?mode=random-etag'
"call $($_): ETag=$($r.Headers['ETag']) Last-Modified=$($r.Headers['Last-Modified'])"
}
headers
body