What are Structured Fields?
RFC 9651, published in September 2024, defines a small set of data types and exact parsing and serialization algorithms for HTTP field values. It obsoletes RFC 8941 (February 2021), the first version, and adds two types to it.
The problem it solves is that every older header has its own grammar. Cache-Control, Accept, Link, Set-Cookie and Content-Disposition each need a hand-written parser, each parser handles edge cases differently, and the differences turn into bugs and security holes. A new field defined as a Structured Field instead says “this is a Dictionary whose values are Integers”, and every conformant library parses it identically.
It is for new fields. You cannot retrofit it onto an existing header whose grammar differs, and a field can only use it for its entire value, never part of it.
1. The three top-level types
A field definition picks exactly one top-level type:
| Type | Shape | Example |
|---|---|---|
| List | Ordered members, comma-separated | Example-List: sugar, tea, rum |
| Dictionary | Ordered key=value members, comma-separated |
Example-Dict: en="Applepie", da=:w4ZibGV0w6ZydGU=: |
| Item | A single bare value | Example-Integer: 5 |
Members of Lists and Dictionaries are Items or Inner Lists, which are space-separated Items in parentheses: ("foo" "bar"). Dictionary keys and parameter keys are lowercase; uppercase letters fail parsing.
Both Lists and Dictionaries can be split across several field lines and are recombined with a comma, so these are equivalent:
Example-Dict: foo=1, bar=2
Example-Dict: foo=1Example-Dict: bar=2An empty List or Dictionary is sent by omitting the field.
2. Parameters
Any Item or Inner List can carry parameters: an ordered map of ;key=value pairs whose values are bare items. Parameters are how fields attach metadata to a member without inventing nested syntax.
Example-List: abc;a=1;b=2; cde_456, (ghi;jk=4 l);q="9";r=wA Boolean true is serialized by omitting the value, so ;a means a=?1. In a Dictionary the same rule applies to member values: Example-Dict: a=?0, b, c; foo=bar makes b and c true.
3. Bare item types
| Type | Serialization | Example | Notes |
|---|---|---|---|
| Integer | Digits, optional - |
42 |
At most 15 digits |
| Decimal | Digits . digits |
4.5 |
At most 12 integer and 3 fractional digits |
| String | Double-quoted | "hello world" |
Printable ASCII only; escape only " and \ |
| Token | Unquoted word | foo123/456 |
Starts with a letter or * |
| Byte Sequence | Base64 between colons | :cHJldGVuZA==: |
|
| Boolean | ?1 or ?0 |
?1 |
|
| Date | @ then Integer seconds |
@1659578233 |
Added in RFC 9651 |
| Display String | % then double-quoted, percent-encoded UTF-8 |
%"f%c3%bc%c3%bc" |
Added in RFC 9651 |
Date is a Unix timestamp in seconds, excluding leap seconds. It gives new fields a compact alternative to the HTTP-date format used by Date and Expires, which recipients must still parse in three historical forms. Deprecation, defined in RFC 9745, is one field that uses it.
Display String carries Unicode for text shown to people. Strings remain ASCII-only on purpose, and the RFC says not to use a Display String where a String or Token would do, because Unicode brings normalization and homograph problems.
The integer limit is not arbitrary: 15 digits fit exactly in an IEEE 754 double, so JavaScript can hold any valid Integer without loss. If you need a larger number, the RFC suggests a String, a Byte Sequence, or a scaling parameter.
4. Strict parsing
The only error handling RFC 9651 defines is failure: if any part of a field value does not parse, the whole field is ignored. A field definition can add constraints but cannot loosen this. The RFC is explicit that a parser trying to be helpful with bad input makes interoperability worse, because it pressures everyone else to copy its leniency.
Two practical consequences. First, if several components append members to the same field, one malformed member discards all of them. Second, test your serializer, not just your parser: a stray uppercase key, a single-quoted string or a fourth decimal place will silently remove the field from the recipient’s view.
Use a library. Mature implementations exist for most languages, and hand-rolled string concatenation is how the errors above get in.
5. Fields that use it
The IANA HTTP Field Name registry now has a Structured Type column. Fields you are likely to meet in API work:
| Field | Type | Defined in |
|---|---|---|
Signature-Input, Signature, Accept-Signature |
Dictionary | RFC 9421 |
Content-Digest, Repr-Digest |
Dictionary | RFC 9530 |
Cache-Status |
List | RFC 9211 |
Proxy-Status |
List | RFC 9209 |
CDN-Cache-Control |
Dictionary | RFC 9213 |
Priority |
Dictionary | RFC 9218 |
Deprecation |
Item (Date) | RFC 9745 |
RateLimit, RateLimit-Policy |
List | Internet-Draft draft-ietf-httpapi-ratelimit-headers |
The RateLimit fields are still an Internet-Draft and are not yet in the registry. Their exact parameters may change before publication; the choice of a Structured Field List is not expected to.
If you are defining a custom header for your own API, define it as a Structured Field. You get a precise definition in a sentence, parsers in every language, and no argument later about whether a value may contain a comma.