<!--
title: HTTP header spec compliance
domain: cloud-infra
topic: Networking
language: TypeScript
source: honojs/hono
updated: 2026-05-21
url: https://awesomereviewers.com/reviewers/hono-http-header-spec-compliance/
-->

When reading, writing, or forwarding HTTP headers, follow RFC semantics instead of naive string checks. For multi-value headers (Accept-Encoding, Vary, Connection, Cache-Control, etc.), parse them properly by splitting on commas, trimming whitespace, and handling parameters such as q-values and wildcards rather than relying on `.includes()` or substring checks, which can produce false positives/negatives (e.g. matching `x-gzip` as `gzip`, or missing a `q=0` exclusion). When a header may already carry values (such as `Vary` or `Connection`), merge new values into the existing set instead of unconditionally overwriting it, e.g.:

```ts
const vary = new Set<string>(c.res.headers.get('Vary')?.split(/\s*,\s*/) ?? [])
vary.add('Origin')
set('Vary', [...vary].join(', '))
```

Use canonical header names as defined by the spec (e.g. `ETag`, not `Etag`), and rely on the `Headers` API's built-in case-insensitive normalization rather than manually lowercasing names before calling `get`/`set`/`delete`. When stripping or retaining headers (e.g. on 304 responses, or when proxying removes `Content-Encoding`/`Content-Length`/`Content-Range`), make sure the logic matches the actual semantics implied by the spec — for example, `Content-Length` must be dropped if the body was decompressed/changed, but headers like `Content-Range` should be preserved unless they are truly invalid for the given response. Prefer presence checks (`headers.has(...)`) over value-based checks when only existence matters. Finally, when merging configuration objects that include headers (e.g. building a `RequestInit` from both an incoming `Request` and user-supplied overrides), be explicit about spread order so that user overrides take precedence without accidentally clobbering computed values like `headers`, `body`, or `duplex`.
