Are HTTP field names case-sensitive, and if the same field name appears on several lines of one message, how should a recipient interpret it? Give an example where the usual rule does not hold.
answer
- Names case-insensitive; HTTP/2 requires lowercase
- Repeat allowed only for #list fields
- Join with commas, keep the order
- Set-Cookie never folds (commas in Expires)
- First-vs-last occurrence = filter bypass
basics
~20 sField names are case-insensitive, so Content-Type and content-type are the same field (HTTP/2 and HTTP/3 require lowercase on the wire). Repeated lines of one field may be joined into a single comma-separated value, but only for fields defined as comma-separated lists. Set-Cookie is the exception: it must stay as separate lines.
solid answer
~50 s**Case:** field names are case-insensitive by definition. `Accept-Encoding`, `accept-encoding` and `ACCEPT-ENCODING` are one field, and any conforming API that reads headers does so case-insensitively. HTTP/2 and HTTP/3 go further and require names to be sent lowercase; an uppercase byte in a name is a protocol error. Field *values* are generally case-sensitive unless the field's own definition says otherwise. **Repetition:** a field may appear more than once only when its value is defined as a comma-separated list. In that case, a recipient may combine the lines, in the order received, into one value joined by commas, without changing the meaning. So `Accept: text/html` plus `Accept: application/json` is equivalent to `Accept: text/html, application/json`. Order is significant and must be preserved. **The exception:** `Set-Cookie` is not list-valued — its syntax uses commas inside dates — so multiple cookies must be sent as multiple lines and must never be combined. Any header API that returns a single joined string mangles it, which is why real APIs expose a multi-value accessor.
code
http · 4 linesAccept-Encoding: gzip
Accept-Encoding: br
Set-Cookie: sid=abc; Path=/; HttpOnly
Set-Cookie: theme=dark; Expires=Wed, 21 Oct 2026 07:28:00 GMTgo deeper
Say names are case-insensitive and that some fields legitimately appear multiple times, with Set-Cookie as the well-known one that must stay separate.
Give the list-valued combining rule precisely, note that order is preserved, and explain why Set-Cookie is exempt (commas in Expires).
Raise the first-versus-last-occurrence bypass risk between a filter and the application, and require rejection of duplicated non-list fields.
Set a normalisation policy: one canonical parse at the edge, explicit rules for duplicates, and library choices that expose multi-value accessors so folding bugs cannot occur.
## Case-insensitivity HTTP field names are case-insensitive. The specification defines names as tokens compared without regard to case, so `Host`, `host` and `HoSt` denote the same field. This is not a courtesy extended by servers; it is the definition, and code that does `headers["Content-Type"]` against a case-sensitive map is broken for any peer that capitalises differently. Two consequences worth stating: - **Store headers in a case-insensitive structure.** Every mature HTTP library does (Java's `HttpHeaders`, Node's lowercased `req.headers`, Go's canonicalising `textproto.MIMEHeader`). Hand-rolled parsing and naive proxy rules — including WAF and access-control rules written against a literal name — are where case bugs and bypasses appear. - **HTTP/2 and HTTP/3 require lowercase.** Names are transmitted lowercase, and receiving an uppercase character in a field name is treated as malformed. So on the wire in modern versions the question is settled; case-insensitive handling remains necessary because HTTP/1.1 peers still send mixed case, and because a gateway translating between versions must not assume the source casing. Field *values* are a separate matter: they are opaque octets whose case sensitivity is decided by each field's grammar. Media types and encoding tokens compare case-insensitively; an `ETag`, a URL path in `Location`, or a cookie value do not. ## Repeated field lines A message may carry the same field name on multiple lines, but only if that field's value is defined as a comma-separated list (the specification's `#list` construct). Then the following equivalence holds: the multiple lines, concatenated in the order received with a comma between them, are semantically identical to a single line with that combined value, and vice versa. `Accept-Encoding: gzip` followed by `Accept-Encoding: br` means the same as `Accept-Encoding: gzip, br`. Three points follow: 1. **Order is meaningful.** Combining must preserve the order of receipt. `Via` and `Forwarded` record the path of a message; reordering them rewrites history. Preference-ordered lists such as `Accept` with q-values likewise depend on their sequence. 2. **Non-list fields must not repeat.** A second `Content-Length`, `Host`, or `Content-Type` is not additional information; it is a malformed message. Recipients reject conflicting `Content-Length` values, and duplicate `Host` fields are rejected outright because they enable routing confusion. 3. **Combining is a recipient's option, not an obligation.** A proxy may forward the lines as it found them. Application code must therefore be prepared for either shape, which is precisely why library APIs expose both `get` (first or joined) and `getAll` (list) accessors. ## The Set-Cookie exception `Set-Cookie` is the canonical field that looks list-like but is not. Its value can legally contain commas — for example inside an `Expires` date such as `Expires=Wed, 21 Oct 2026 07:28:00 GMT` — so joining two `Set-Cookie` lines with a comma produces a string that cannot be split back apart unambiguously. The cookie specification therefore requires each cookie on its own line, and requires recipients not to fold them. Any framework whose header map returns a single string per name will corrupt multiple cookies unless it special-cases this field; the practical rule is: always use a multi-value accessor for `Set-Cookie`, and never write generic header-folding middleware without excluding it. A smaller cousin: `WWW-Authenticate` is list-valued in principle but its challenge parameters also contain commas, so naive splitting on commas breaks multi-scheme challenges too. ## Security angle Header-handling inconsistency is a bypass vector. If a security filter looks at the first occurrence of a field and the application looks at the last, or one lowercases names and the other does not, an attacker supplies both and gets different views on the two sides. The defence is the same as for framing ambiguity: normalise once, at a single trusted point, reject duplicates of non-list fields, and never resolve ambiguity by guessing. ## What a good answer contains Case-insensitive names plus the HTTP/2 lowercase requirement; the list-valued combining rule with order preservation; the fact that non-list fields must not repeat; and `Set-Cookie` as the exception with the reason (commas in dates), not just as a memorised fact.
- Why can Set-Cookie not be folded into one comma-separated line?Because its value is not defined as a comma-separated list and can itself contain commas, most obviously in an Expires date. Folding produces a string that cannot be reliably split back into individual cookies. The cookie specification therefore mandates one line per cookie and forbids recipients from combining them.
- A request contains two Host header fields. What should happen?The request must be rejected, typically with 400. Host is not list-valued, and two values create an ambiguity about which authority the request addresses. That ambiguity is directly exploitable when a proxy routes on one value and the origin serves the other, so the correct handling is rejection rather than choosing a winner.
saying these in an interview costs you the question
- Looking up headers in a case-sensitive map
- Assuming any repeated header can be safely joined with commas
- Joining Set-Cookie values into a single string
- Believing HTTP/2's lowercase rule means HTTP field names became case-sensitive
- Accepting duplicate Host or Content-Length fields and picking one