19

Postel's Law: repealed

#
  • author’s position
  • opinionated
  • fragment

Conflicts with: Postel ↔ schema evolution

Primary sources: RFC 9413 — Maintaining Robust Protocols

“Be liberal in what you accept” gave us XSS, request smuggling, and protocols that cannot evolve. The modern position: strict validation at the boundary, rejection instead of guessing.

The principle is Jon Postel’s, from the early Internet specifications; RFC 793 (TCP, 1981) phrases it as “be conservative in what you do, be liberal in what you accept from others”. In a young network of independent implementations it helped them interoperate despite each other’s bugs. The costs showed up over decades. If receivers accept malformed input, senders never learn it is malformed, the bugs become the de facto protocol (16), and every new implementation has to reproduce them. Eric Allman, “The Robustness Principle Reconsidered” (ACM Queue, 2011), made the security case: liberal parsing is where attackers live.

Request smuggling is the textbook case. A front-end proxy and a back-end server each “tolerate” a request with both Content-Length and Transfer-Encoding, but resolve the ambiguity differently; the attacker’s bytes that the proxy saw as one request body, the back end reads as the start of a second request. Two lenient parsers that disagree are a vulnerability. The current HTTP/1.1 specification (RFC 9112) says such a message ought to be handled as an error.

An honest note on the title. RFC 9413 is titled “Maintaining Robust Protocols” and formally repeals nothing: it shows that liberal acceptance itself becomes a source of ossification and vulnerabilities, and recommends actively managing tolerance rather than banning it. “Repealed” is this page’s editorial position, not the text of the RFC.

Code example (fragment)
fragment
Python · pydantic v2
# strict mode at the external boundary
class PaymentIn(BaseModel):
    model_config = ConfigDict(strict=True, extra="forbid")  # "123" will not become 123

    amount_cents: int
    currency: Literal["EUR", "USD"]
Go
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
// unknown field from an external client = 400, not a silent drop

Where tolerance still belongs. Strict is not the same as brittle. Ignoring unknown optional fields in a versioned schema is how producers evolve without breaking consumers (18) — that is designed extensibility, declared in the contract, not guessing. The line: tolerate what the specification says may vary; reject what it says is wrong, and parse into a typed value once, at the edge (2).

Last technical review: 2026-08-04 · Reference · 112 principles