A typed grammar for HTTP headers
Historically every HTTP header invented its own ad-hoc syntax — comma-separated values, semicolons, equals signs, quoted strings — making parsing fragile and error-prone. RFC 8941 Structured Field Values defines a small, typed grammar so new headers can be parsed uniformly by any implementation. This reference explains the three top-level types, the bare-item types and parameter syntax, with a live parser that classifies any value you paste.
Why structured fields matter
Before RFC 8941, HTTP header parsers were written per-header because each had its own quirks. Accept uses quality values with ;q=0.9. Cache-Control uses tokens and integers with =. Cookie uses ; as a separator. Every one was a bespoke parser. Structured fields give new headers a shared grammar with formal type safety. Libraries exist for HTTP/2 and HTTP/3 implementations that can parse any structured-field header without header-specific knowledge. Headers like Cache-Status, Priority, Repr-Digest, Sec-CH-UA and many others now use this format.
The three top-level types
A header’s defining RFC declares exactly one of three types:
Item
A single bare item, optionally with parameters:
Content-Length: 42
Sec-CH-UA-Mobile: ?0
List
A comma-separated sequence of items or inner-lists:
Accept-Encoding: gzip, br;q=0.8, *;q=0.1
Sec-CH-UA: "Chromium";v="124", "Google Chrome";v="124"
An inner list wraps several items in parentheses as one slot in the outer list:
Example-List: (item1 item2);param, item3
Dictionary
A comma-separated sequence of key=value pairs:
Cache-Status: ExampleCache; hit, UpstreamCache; fwd=uri-miss
Priority: u=1, i
Repr-Digest: sha-256=:abc123==:
Bare-item types
Each member or key-value is one bare-item type:
| Type | Syntax | Example |
|---|---|---|
| Integer | Unquoted digits, no decimal point | 42, -3 |
| Decimal | Digits with decimal point, up to 3 fractional digits | 1.5, 0.800 |
| String | Double-quoted printable ASCII | "text/html" |
| Token | Unquoted, starts with a letter or * | gzip, no-cache |
| Byte Sequence | Base64 wrapped in colons | :cHF=: |
| Boolean | ?1 for true, ?0 for false | ?1, ?0 |
Parameters
Parameters append to any item or inner-list member with ;key or ;key=value:
gzip;q=0.9 (q is a Decimal parameter)
"Chrome";v="124" (v is a String parameter)
sni (bare key, shorthand for sni=?1, Boolean true)
Multiple parameters chain: ;a=1;b=?0;c=token.
How the parser works
The live parser detects the top-level type heuristically: a top-level = outside quotes implies a Dictionary; a comma at the top level (not inside an inner-list or string) implies a List; otherwise it is an Item. It then recursively classifies each member and parameter by its value shape.
Practical tips
- Strings vs Tokens: Tokens are more compact but only accept
[A-Za-z0-9_!#$%&'*+.^_\|~-]. Use a String for anything with special characters like spaces or/`. - Byte sequences are for opaque binary values (digests, keys).
:cHF=:wraps the padded base64. - Decimals are limited to three fractional digits; if you need more precision, encode the value as a String.
- New headers should use structured fields — it lets HTTP tooling, CDNs and debugging proxies parse them without custom code.