{
  "what": "Every documented behaviour of this service as one flat list, so a test harness can consume the catalogue without scraping eight family indexes. One row per behaviour: where it lives, what to send to observe it, what the defect is, which document says so, and whether the bytes are stable enough to pin a digest on.",
  "jsonl": "https://badhttp.dev/corpus.jsonl",
  "format": "application/x-ndjson — one JSON object per line, newline-terminated",
  "count": 138,
  "by_layer": {
    "message-framing": 2,
    "timing": 2,
    "diagnostic": 2,
    "status-codes": 1,
    "body-syntax": 16,
    "event-streams": 14,
    "range-requests": 14,
    "conditional-requests": 10,
    "cookies": 19,
    "authentication": 18,
    "content-coding": 21,
    "payment": 19
  },
  "license": "CC0-1.0",
  "license_url": "https://badhttp.dev/license",
  "rfc9112_message_syntax": {
    "emits_syntax_violations": false,
    "probed": "2026-09-06",
    "method": "151 documented endpoints captured over TLS with ALPN http/1.1, each response head parsed byte by byte and judged against RFC 9112 §2.2 (bare CR/LF), §4 (status-line: HTTP-version, 3DIGIT status-code, reason-phrase charset), §5 (field-line shape), §5.1 (field-name is a token; no whitespace before the colon), §5.2 (obs-fold), §5.5 (field-value charset) and §6 (Content-Length and Transfer-Encoding agreement, duplicate and non-numeric Content-Length).",
    "result": "0 of 151 responses violated RFC 9112 message syntax.",
    "why": "This Worker runs behind Cloudflare, which re-serializes every response before it reaches you. Neither workerd nor the edge will emit a malformed start-line, a malformed field-line, an obs-fold, a bare CR or LF in the header section, or a Content-Length that disagrees with a Transfer-Encoding. A header value set to a string with a CR in it does not reach the wire as one. This is a property of the platform, not a design choice, and it is not something this service can opt out of.",
    "what_this_means_for_you": "If you are testing a parser against the RFC 9112 grammar or the request-smuggling surface, badhttp.dev is not a source of that traffic and capturing from it will teach you nothing: you would collect 151 well-formed messages. Worse, a corpus of clean messages can read as evidence that a checker has no false negatives. Serve those cases from a raw socket you control (a listener writing literal bytes), not from anything behind a CDN.",
    "where_we_do_misbehave": "One layer up. RFC 9112 §6.3 completeness: /truncate and /sse/drop declare a Content-Length and then deliver fewer bytes and close (verified 2026-09-06: 1000 declared / 500 delivered, and 565 declared / 53 delivered). Everything else in this catalogue is a semantic defect inside a well-formed message — a wrong status code, a lying Content-Range, a corrupt gzip member, a malformed auth challenge, a cookie attribute no jar should accept."
  },
  "how_to_use": [
    "GET /corpus.jsonl once. Each row is self-contained and carries a ready-to-run curl.",
    "Send the request_headers. On /compress they are load-bearing — see the capture note.",
    "Capture what you receive; this service ships recipes and expectations, never pre-recorded bytes, because a CDN sits between this Worker and you and the honest artifact is the one you captured yourself.",
    "deterministic_bytes tells you whether a digest pinned today will still match tomorrow. varies_by names what moves it.",
    "Nothing here requires attribution. See /license."
  ],
  "capture_notes": {
    "compress": "THE ONE THAT BITES: what you receive depends on your Accept-Encoding, because the edge removes one coding layer it recognizes unless your normalized set lists it — no-transform notwithstanding. Send Accept-Encoding: gzip to capture the flavor as this server sends it. Send no Accept-Encoding to capture what the edge does to it, which is the second half of the documentation and a real defect class of its own. Both are worth having; they are different fixtures and a corpus should say which one it holds.",
    "auth": "Digest flavors carry a nonce derived from a 5-minute clock bucket, so their heads are not byte-stable across captures."
  },
  "stability_legend": {
    "deterministic_bytes": "true means the same request returns the same body bytes. It never means the response HEAD is byte-stable: Date, cf-ray and Server-Timing move on every response and are the edge's, not ours.",
    "varies_by": "What changes the response: request headers you control, the clock, or timing."
  },
  "not_covered": {
    "rfc9112-message-syntax": "If you are testing a parser against the RFC 9112 grammar or the request-smuggling surface, badhttp.dev is not a source of that traffic and capturing from it will teach you nothing: you would collect 151 well-formed messages. Worse, a corpus of clean messages can read as evidence that a checker has no false negatives. Serve those cases from a raw socket you control (a listener writing literal bytes), not from anything behind a CDN.",
    "request-smuggling": "Same reason. This service cannot emit conflicting framing headers; the edge normalizes them away.",
    "http/2 and http/3 framing": "Both are served, but the frame layer is Cloudflare's, not ours; nothing here misbehaves at it on purpose."
  }
}
