- From: James M Snell <notifications@github.com>
- Date: Mon, 20 Jul 2026 17:38:55 -0700
- To: whatwg/fetch <fetch@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/fetch/pull/1943@github.com>
Adds new APIs to the Headers class for getting/setting structured header fields.
Structured fields are defined in RFC 8941. Newer HTTP header definitions build on it. Fetch's handling of all header values as strings works but loses some of the utility. This commit adds new `getStructured/setStructured` APIS to `Headers` for getting/setting header field values as structured fields. The existing `get`/`set`/`append`/etc are left untouched. Header iteration is left untouched. It remains possible to get all fields as strings.
There are currently ~36 standard headers that use structured header fields:
#### Dictionary
| Header | Spec | Description |
| ------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
| `Priority` | [RFC 9218](https://www.rfc-editor.org/rfc/rfc9218) | HTTP response prioritization (urgency, incremental delivery) |
| `Signature` | [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) | HTTP message signatures |
| `Signature-Input` | [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) | Metadata for message signatures (covered components, key ID, etc.) |
| `Accept-Signature` | [RFC 9421 §5.1](https://www.rfc-editor.org/rfc/rfc9421#section-5.1) | Requests that recipient apply a signature |
| `Content-Digest` | [RFC 9530](https://www.rfc-editor.org/rfc/rfc9530) | Integrity digest over HTTP message content |
| `Repr-Digest` | [RFC 9530](https://www.rfc-editor.org/rfc/rfc9530) | Integrity digest over HTTP representation |
| `Want-Content-Digest` | [RFC 9530](https://www.rfc-editor.org/rfc/rfc9530) | Requests `Content-Digest` with algorithm preferences |
| `Want-Repr-Digest` | [RFC 9530](https://www.rfc-editor.org/rfc/rfc9530) | Requests `Repr-Digest` with algorithm preferences |
| `CDN-Cache-Control` | [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213) | Targeted cache directives for CDN caches |
| `Use-As-Dictionary` | [RFC 9842](https://www.rfc-editor.org/rfc/rfc9842) | Marks a response as a compression dictionary |
#### List
| Header | Spec | Description |
| ------------------------ | --------------------------------------------------- | --------------------------------------------------- |
| `Cache-Status` | [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211) | Per-cache handling report (`hit`, `fwd`, `ttl`, etc.) |
| `Proxy-Status` | [RFC 9209](https://www.rfc-editor.org/rfc/rfc9209) | Per-intermediary handling report with error details |
| `Accept-CH` | [RFC 8942](https://www.rfc-editor.org/rfc/rfc8942) | Advertises server support for Client Hints |
| `Client-Cert-Chain` | [RFC 9440](https://www.rfc-editor.org/rfc/rfc9440) | Client certificate chain from TLS-terminating proxy |
| `Accept-Query` | [RFC 10008](https://www.rfc-editor.org/rfc/rfc10008) | Accepted media types for HTTP QUERY body |
| `Cache-Groups` | [RFC 9875](https://www.rfc-editor.org/rfc/rfc9875) | Associates cached responses with named groups |
| `Cache-Group-Invalidation` | [RFC 9875](https://www.rfc-editor.org/rfc/rfc9875) | Invalidates all responses in named cache groups |
#### Item
| Header | Spec | Description |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| `Client-Cert` | [RFC 9440](https://www.rfc-editor.org/rfc/rfc9440) | End-entity client certificate (Byte Sequence) |
| `Capsule-Protocol` | [RFC 9297](https://www.rfc-editor.org/rfc/rfc9297) | Enables the Capsule Protocol on an HTTP stream (Boolean) |
| `Deprecation` | [RFC 9745](https://www.rfc-editor.org/rfc/rfc9745) | Signals resource deprecation (Date) |
| `Available-Dictionary` | [RFC 9842](https://www.rfc-editor.org/rfc/rfc9842) | Client has a compression dictionary available |
| `Dictionary-ID` | [RFC 9842](https://www.rfc-editor.org/rfc/rfc9842) | Assigns a stable ID to a compression dictionary response |
| `Concealed-Auth-Export` | [RFC 9729](https://www.rfc-editor.org/rfc/rfc9729) | Exported keying material for concealed HTTP auth |
| `Cross-Origin-Embedder-Policy` | [HTML Standard](https://html.spec.whatwg.org/multipage/origin.html#coep) | Controls cross-origin resource loading policy |
| `Cross-Origin-Embedder-Policy-Report-Only` | [HTML Standard](https://html.spec.whatwg.org/multipage/origin.html#coep) | COEP in report-only mode |
| `Cross-Origin-Opener-Policy` | [HTML Standard](https://html.spec.whatwg.org/multipage/browsers.html#cross-origin-opener-policies) | Controls browsing context group sharing |
| `Cross-Origin-Opener-Policy-Report-Only` | [HTML Standard](https://html.spec.whatwg.org/multipage/browsers.html#cross-origin-opener-policies) | COOP in report-only mode |
| `Origin-Agent-Cluster` | [HTML Standard](https://html.spec.whatwg.org/multipage/origin.html#origin-agent-cluster) | Requests origin-keyed agent cluster (Boolean) |
| `Sec-Fetch-Dest` | [Fetch Metadata](https://w3c.github.io/webappsec-fetch-metadata/) | Request destination type (Token) |
| `Sec-Fetch-Mode` | [Fetch Metadata](https://w3c.github.io/webappsec-fetch-metadata/) | Request mode (Token) |
| `Sec-Fetch-Site` | [Fetch Metadata](https://w3c.github.io/webappsec-fetch-metadata/) | Request-vs-target origin relationship (Token) |
| `Sec-Fetch-User` | [Fetch Metadata](https://w3c.github.io/webappsec-fetch-metadata/) | User activation (Boolean) |
| `Sec-Purpose` | [Fetch Standard](https://fetch.spec.whatwg.org/) | Request purpose, e.g. `prefetch` (Token) |
### Examples
#### Reading the `Priority` header (Dictionary)
```js
// Priority: u=0, i
const p = response.headers.getStructured("Priority", "dictionary");
const urgency = p?.get("u")?.value ?? 3; // 0
const incremental = p?.get("i")?.value ?? false; // true
```
Compared with strings:
```js
const raw = response.headers.get("Priority"); // "u=0, i"
// ... now what? Split on comma? Parse key=value? Handle quoting?
// Every app rolls its own parser and gets edge cases wrong.
```
#### Reading Cache-Status (List)
```js
// Cache-Status: ReverseProxy;hit, CDN;fwd=miss;stored;ttl=3600
const cs = response.headers.getStructured("Cache-Status", "list");
for (const entry of cs) {
console.log(entry.value); // "ReverseProxy", "CDN"
console.log(entry.params.get("hit")); // true, undefined
console.log(entry.params.get("fwd")); // undefined, "miss"
console.log(entry.params.get("ttl")); // undefined, 3600
}
```
#### Reading Content-Digest (Dictionary with Byte Sequences)
```js
// Content-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
const digest = response.headers.getStructured("Content-Digest", "dictionary");
const hash = digest?.get("sha-256")?.value; // Uint8Array
```
#### Reading Sec-Purpose (Item)
```js
// Sec-Purpose: prefetch
const purpose = request.headers.getStructured("Sec-Purpose", "item");
if (purpose?.value === "prefetch") {
// serve a lighter response
}
```
#### Writing Priority (Dictionary — plain object form)
```js
// Sets: Priority: u=0, i
request.headers.setStructured("Priority", "dictionary", {
u: { value: 0 },
i: { value: true }
});
```
#### Writing Cache-Status (List with parameters)
```js
// Sets: Cache-Status: MyProxy;hit;ttl=7200
response.headers.setStructured("Cache-Status", "list", [
{ value: "MyProxy", params: { hit: true, ttl: 7200 } }
]);
```
#### Writing Content-Digest (Dictionary with Byte Sequence)
```js
const body = await response.arrayBuffer();
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", body));
// Sets: Content-Digest: sha-256=:base64encodedhash=:
response.headers.setStructured("Content-Digest", "dictionary", {
"sha-256": { value: hash }
});
```
#### Graceful fallback when unsupported
```js
// getStructured returns null if the header is absent, malformed,
// or if the implementation doesn't support structured field parsing.
const priority = request.headers.getStructured("Priority", "dictionary");
const urgency = priority?.get("u")?.value ?? 3; // always works, defaults to 3
```
#### Token vs String serialization
```js
// Strings matching token syntax serialize as unquoted tokens:
headers.setStructured("Example", "item", { value: "foo" });
// Sets: Example: foo
// Strings that don't match token syntax serialize as quoted strings:
headers.setStructured("Example", "item", { value: "hello world" });
// Sets: Example: "hello world"
```
#### All input forms for dictionaries and parameters (HeadersInit pattern)
```js
// Plain object (most ergonomic)
headers.setStructured("Priority", "dictionary", {
u: { value: 3 },
i: { value: true }
});
// Map (preserves insertion order explicitly)
headers.setStructured("Priority", "dictionary", new Map([
["u", { value: 3 }],
["i", { value: true }]
]));
// Sequence of pairs
headers.setStructured("Priority", "dictionary", [
["u", { value: 3 }],
["i", { value: true }]
]);
// Parameters accept the same forms:
headers.setStructured("Cache-Status", "list", [
{ value: "cdn", params: { hit: true, ttl: 3600 } }, // plain object
{ value: "origin", params: new Map([["fwd", "miss"]]) }, // Map
{ value: "edge", params: [["stored", true]] } // sequence
]);
```
---
- [ ] At least two implementers are interested (and none opposed):
* …
* …
- [ ] [Tests](https://github.com/web-platform-tests/wpt) are written and can be reviewed and commented upon at:
* … <!-- If these tests are tentative, link a PR to make them non-tentative. -->
- [ ] [Implementation bugs](https://github.com/whatwg/meta/blob/main/MAINTAINERS.md#handling-pull-requests) are filed:
* Chromium: …
* Gecko: …
* WebKit: …
* Deno (not for CORS changes): …
- [ ] [MDN issue](https://github.com/whatwg/meta/blob/main/MAINTAINERS.md#handling-pull-requests) is filed: …
- [ ] The top of this comment includes a [clear commit message](https://github.com/whatwg/meta/blob/main/COMMITTING.md) to use. <!-- If you created this PR from a single commit, Github copied its message. Otherwise, you need to add a commit message yourself. -->
(See [WHATWG Working Mode: Changes](https://whatwg.org/working-mode#changes) for more details.)
<!--
This comment and the below content is programmatically generated.
You may add a comma-separated list of anchors you'd like a
direct link to below (e.g. #idl-serializers, #idl-sequence):
Don't remove this comment or modify anything below this line.
If you don't want a preview generated for this pull request,
just replace the whole of this comment's content by "no preview"
and remove what's below.
-->
***
### :boom: Error: 422 Unprocessable Entity :boom: ###
[PR Preview](https://github.com/tobie/pr-preview#pr-preview) failed to build. _(Last tried on Jul 21, 2026, 12:38 AM UTC)_.
<details>
<summary>More</summary>
PR Preview relies on a number of web services to run. There seems to be an issue with the following one:
:rotating_light: [Spec Generator](https://www.w3.org/publications/spec-generator/) - Spec Generator is the web service used to build bikeshed/ReSpec specs
:link: [Related URL](https://www.w3.org/publications/spec-generator/?type=bikeshed-spec&output=html&url=https%3A%2F%2Fraw.githubusercontent.com%2Fjasnell%2Ffetch%2F96f7cf5f5e9686c0676f00b43a4078943a322d71%2Ffetch.bs&force=1&md-status=LS-PR&md-Text-Macro=PR-NUMBER%201943)
**Error output:**
```json
[
{
"lineNum": "8762:15",
"messageType": "fatal",
"text": "Saw a [[ opening a biblio or section autolink, but couldn't parse the following contents. If you didn't intend this to be a biblio autolink, escape the initial [ as &bs[;"
},
{
"lineNum": "7994:12",
"messageType": "warning",
"text": "The var 'result' (in global scope) is only used once.\nIf this is not a typo, please add an ignore='' attribute to the <var>."
},
{
"lineNum": "8291:16",
"messageType": "warning",
"text": "The var 'bareItem' (in algorithm 'convert a structured field item to a JavaScript object') is only used once.\nIf this is not a typo, please add an ignore='' attribute to the <var>."
},
{
"lineNum": "8003:3",
"messageType": "lint",
"text": "RFC2119 keyword in non-normative section (use: might, can, has to, or override with <span class=allow-2119>): must be one of \""
},
{
"lineNum": "8885:1",
"messageType": "lint",
"text": "RFC2119 keyword in non-normative section (use: might, can, has to, or override with <span class=allow-2119>): Parsing structured fields and converting the result to\nJavaScript objects is entirely optional. Implementations that do not\nsupport structured field parsing are fully compliant with this\nspecification by having "
},
{
"lineNum": "8885:1",
"messageType": "lint",
"text": "RFC2119 keyword in non-normative section (use: might, can, has to, or override with <span class=allow-2119>): method itself is required.\n\n"
},
{
"lineNum": "8929:1",
"messageType": "lint",
"text": "RFC2119 keyword in non-normative section (use: might, can, has to, or override with <span class=allow-2119>): Serializing structured fields is entirely optional.\nImplementations that do not support structured field serialization are\nfully compliant with this specification. The minimum conformance\nrequirement is that "
},
{
"lineNum": null,
"messageType": "failure",
"text": "Did not generate, due to errors exceeding the allowed error level."
}
]
```
_This seems to be an issue with the [Spec Generator](https://www.w3.org/publications/spec-generator/) service. PR Preview doesn't manage this service and so has no control over it. If you've identified an issue with it, you can [report the issue to the maintainers of Spec Generator](https://github.com/w3c/spec-generator/issues/new) directly. Please be courteous. Thank you!_
_If you don't have enough information above to solve the error by yourself or if the issue doesn't seem related to Spec Generator, you can [file an issue with PR Preview](https://github.com/tobie/pr-preview/issues/new?title=Unidentified%20Error&body=See%20whatwg/fetch%231943.)._
</details>
You can view, comment on, or merge this pull request online at:
https://github.com/whatwg/fetch/pull/1943
-- Commit Summary --
* Add getStructured/setStructured to Headers
-- File Changes --
M fetch.bs (760)
-- Patch Links --
https://github.com/whatwg/fetch/pull/1943.patch
https://github.com/whatwg/fetch/pull/1943.diff
--
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/fetch/pull/1943
You are receiving this because you are subscribed to this thread.
Message ID: <whatwg/fetch/pull/1943@github.com>
Received on Tuesday, 21 July 2026 00:39:00 UTC