http2: content-encoding is incorrectly listed in kSingleValueHeaders

Open Beginner friendly
#67,151 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@inoway46 is already working on this.

Since Nov 1, 2026.

  • #67184 by @inoway46 — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
74/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript, node.js

Research direction

Read lib/internal/http2/util.js, focusing on kSingleValueHeaders and the mapToHeaders and buildNgHeaderString paths. Check how duplicate content-encoding values are handled in each direction; done means incoming values are combined in order and outgoing multiple values no longer trigger ERR_HTTP2_HEADER_SINGLE_VALUE.

Written by the indexing model from the issue text.

Description

confirmed-bug http2
Version

v26.13.0

Platform

Subsystem

No response

What steps will reproduce the bug?

Drafted by Gemini 4 Argon.

In lib/internal/http2/util.js, HTTP2_HEADER_CONTENT_ENCODING is listed in kSingleValueHeaders:

  • Incoming (mapToHeaders): When an HTTP/2 peer sends multiple content-encoding header lines (e.g., content-encoding: gzip twice on Google Drive viewer endpoints), only the first value is kept in headers['content-encoding'] and subsequent values are silently dropped (even with strictSingleValueFields: false from #59917). As a result, HTTP/2 clients only decompress the first layer and treat the still-compressed payload as decoded data, and reverse proxies forwarding headers strip the remaining encoding layers so downstream browsers fail to decode the response.
  • Outgoing (buildNgHeaderString): Passing multiple content-encoding values throws ERR_HTTP2_HEADER_SINGLE_VALUE by default, preventing servers and proxies from sending or forwarding multi-value content-encoding headers.
Evidence
  1. HTTP Semantics — RFC 9110 §8.4 (Content-Encoding) & §5.3:

    Content-Encoding = #content-coding

    If one or more encodings have been applied to a representation, the sender that applied the encodings MUST generate a Content-Encoding header field that lists the content codings in the order in which they were applied.

    Because Content-Encoding uses the #element list ABNF production, multiple content-encoding header field lines are valid per RFC 9110 §5.3 and must be combined in order with ', ' rather than discarded.

  2. HTTP/2 & HPACK — RFC 9113 §8.2.1 & RFC 7541:
    HTTP/2 only forbids connection-specific headers (connection, transfer-encoding, keep-alive, upgrade, proxy-connection); for all other fields, RFC 9113 §8.2.1 inherits RFC 9110 §5.3, allowing list-based headers to be transmitted as multiple separate HPACK header field entries (each referencing HPACK static table index 26 content-encoding). nghttp2 itself accepts multiple content-encoding entries and delivers all of them in rawHeaders—the truncation happens purely in Node's JS kSingleValueHeaders check.

  3. node:http consistency: lib/_http_incoming.js (_addHeaderLine) does not treat content-encoding as single-value and joins duplicate lines with ', '.

Removing HTTP2_HEADER_CONTENT_ENCODING from kSingleValueHeaders fixes both directions.

Compatibility
  • No type change: headers['content-encoding'] remains string | undefined (mapToHeaders joins non-single-value headers with ', '), and single content-encoding responses are unaffected.
  • Incoming duplicate headers: headers['content-encoding'] becomes 'gzip, br' instead of 'gzip' (matching what a single content-encoding: gzip, br header or node:http already produces). Callers doing exact single-token checks (headers['content-encoding'] === 'gzip') without splitting on ',' may stop matching when a peer sends multiple lines.
  • Outgoing validation: Passing multiple content-encoding values will succeed instead of throwing ERR_HTTP2_HEADER_SINGLE_VALUE (potentially semver-major per Node's error-removal policy).
How often does it reproduce? Is there a required condition?
What is the expected behavior? Why is that the expected behavior?
What do you see instead?
Additional information

No response

Dominant language
JavaScript
Stars
123k
Forks
39.3k
Avg merge
4d 15h
Merged PRs (30d)
238

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from nodejs/node

All issues in nodejs/node

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.