Proposal Details
Go's HTTP/2 implementation indexes all headers in the HPACK dynamic table by default. This has two primary impacts:
- Go lacks a mechanism to follow the RFC 7541 recommendation to use a "Never Indexed" representation for sensitive secrets like Authorization or X-API-Key.
- Large, unique headers cause table thrashing by frequently evicting useful static headers. This causes fleet-wide CPU overhead and degrades compression efficiency by forcing common headers to be re-transmitted as literals.
I propose adding a SensitiveHeaders []string field to the Transport. This field allows users to specify case-insensitive header names that the HPACK encoder should flag as "Never Indexed." Setting this at the Transport level ensures a consistent policy across all pooled connections. This applies to the client side of the connection.
type Transport struct {
// ... existing fields ...
// SensitiveHeaders specifies header names that should never be
// indexed in HTTP/2 HPACK dynamic tables. Header names are
// matched case-insensitively. This prevents sensitive data
// from being stored in HPACK tables and reduces table thrashing
// from large, unique headers.
SensitiveHeaders []string
}
Alternatives considered:
- We considered a mechanism to set these headers dynamically using a "magic" header or a "Trailer-like" internal signaling trick (e.g.,
Headers.Set("__sensitive", "header-name")). We decided on a global approach because it is less cumbersome for headers that are consistently sensitive across a service.
- Libraries like nghttp2 avoid indexing headers larger than 75% of the table size to prevent thrashing. This could be a separate proposal in the future.
Proposal Details
Go's HTTP/2 implementation indexes all headers in the HPACK dynamic table by default. This has two primary impacts:
I propose adding a
SensitiveHeaders []stringfield to the Transport. This field allows users to specify case-insensitive header names that the HPACK encoder should flag as "Never Indexed." Setting this at the Transport level ensures a consistent policy across all pooled connections. This applies to the client side of the connection.Alternatives considered:
Headers.Set("__sensitive", "header-name")). We decided on a global approach because it is less cumbersome for headers that are consistently sensitive across a service.