What is the difference between an end-to-end HTTP field and a hop-by-hop one, how does the Connection header field designate the hop-by-hop set, and what goes wrong if a proxy forwards them unchanged?
answer
- Connection lists the hop-scoped fields
- Fixed set: Keep-Alive, TE, Trailer, Transfer-Encoding, Upgrade, Proxy-Auth*
- Strip the listed fields, then strip Connection
- Relayed Transfer-Encoding = smuggling primitive
- HTTP/2 bans them; TE: trailers only
basics
~20 sEnd-to-end fields travel from origin sender to final recipient through every proxy. Hop-by-hop fields describe one connection only and must be consumed and removed by each hop. The Connection header field names them, plus a fixed set such as Keep-Alive, Transfer-Encoding, TE, Trailer, Upgrade and the Proxy-Authenticate pair. Forwarding them leaks one hop's connection state onto another and breaks framing or upgrades.
solid answer
~50 sHTTP intermediaries relay messages, so each field has a scope. **End-to-end** fields (`Content-Type`, `ETag`, `Authorization`, `Cache-Control`) are meant for the far end and must survive every hop. **Hop-by-hop** (connection-specific) fields describe the single TCP connection you are talking over and are meaningless — or harmful — on the next one. The `Connection` field carries the list: any field name it mentions is hop-by-hop for that message, and a proxy must delete both those fields and `Connection` itself before forwarding. On top of that, `Keep-Alive`, `Transfer-Encoding`, `TE`, `Trailer`, `Upgrade`, `Proxy-Authenticate` and `Proxy-Authorization` are connection-specific by definition. Forwarding them causes real failures: a relayed `Transfer-Encoding` desynchronises framing when the proxy re-frames the body; a relayed `Upgrade` or `Connection: upgrade` produces half-opened WebSocket state; a relayed `Keep-Alive` advertises the wrong timeout. HTTP/2 and HTTP/3 forbid these fields entirely — `TE: trailers` is the only allowance — so a gateway downgrading to HTTP/1.1 must synthesise them, and one upgrading must strip them.
code
http · 12 linesGET /stream HTTP/1.1
Host: app.example.com
Connection: keep-alive, X-Edge-Hint
Keep-Alive: timeout=5
X-Edge-Hint: pool-b
Authorization: Bearer eyJ...
--- forwarded upstream ---
GET /stream HTTP/1.1
Host: app.example.com
Authorization: Bearer eyJ...
Via: 1.1 edgego deeper
Know that some fields are for the next hop only and that proxies remove them, with Connection as the marker.
Name the fixed set, explain that Connection lists additional hop-scoped fields, and give one concrete breakage such as WebSocket upgrades.
Connect it to framing safety: a relayed Transfer-Encoding is a smuggling primitive, and proxy header policy should be an allow-list, not a copy-all.
Own the boundary policy across versions: what the edge injects, strips and regenerates on downgrade, and how that policy is enforced and tested rather than assumed.
## Two scopes for one message An HTTP message may pass through caches, reverse proxies, load balancers and gateways. Each hop is a separate connection with its own capabilities: one may be HTTP/1.1 with keep-alive and gzip transfer coding, the next HTTP/2 over TLS. So fields split by scope. **End-to-end fields** belong to the message and its representation, and must be relayed unchanged (with a few defined exceptions, such as caches adding `Age` or `Via`). `Content-Type`, `ETag`, `Cache-Control`, `Authorization`, `Location` are all end-to-end: dropping or rewriting them changes what the far end sees. **Hop-by-hop, or connection-specific, fields** describe the connection currently in use. They are consumed by the recipient of that connection and must not be forwarded. The classic set is `Connection`, `Keep-Alive`, `Transfer-Encoding`, `TE`, `Trailer`, `Upgrade`, `Proxy-Authenticate` and `Proxy-Authorization`. ## How Connection designates the set `Connection` does double duty. It carries connection options (`close`, and historically `keep-alive`), and it *names other fields* that are connection-specific for this message. `Connection: keep-alive, X-Route-Hint` means both `Keep-Alive` and `X-Route-Hint` are scoped to this hop. A conforming intermediary must remove every field named in `Connection`, then remove `Connection` itself, before forwarding. That is the extension mechanism for hop scope: it lets a client and its immediate peer agree on a private field without the risk of it leaking onward. It is also why blindly copying all inbound fields to an upstream request — a very common shortcut in hand-written proxies and API gateways — is wrong. ## What breaks when they are forwarded 1. **Framing corruption.** `Transfer-Encoding` describes how the body is encoded on *this* connection. If a proxy buffers a chunked request and forwards it with `Content-Length` while also copying the original `Transfer-Encoding: chunked`, the upstream sees both fields and the two ends can disagree about the message boundary. That is a request-smuggling primitive, and it is one of the most common ways a homegrown proxy becomes exploitable. 2. **Broken upgrades.** `Upgrade` plus `Connection: upgrade` negotiates a protocol switch on one connection, typically WebSocket. Relaying it to a peer that never agreed leaves one side expecting frames and the other expecting HTTP. Conversely, a proxy that strips `Upgrade` without deliberately proxying the tunnel breaks WebSocket entirely — which is why proxy configurations for WebSocket explicitly re-add these two fields for that route rather than passing everything. 3. **Wrong connection parameters.** A forwarded `Keep-Alive: timeout=5` advertises the previous hop's idle timeout to a peer with different settings, producing races where one side closes a connection the other is about to reuse. 4. **Credential leakage across trust boundaries.** `Proxy-Authorization` is for the proxy, not the origin. Forwarding it hands proxy credentials to an upstream that has no business seeing them. ## Modern versions HTTP/2 and HTTP/3 remove the concept from the wire: connection-specific fields are forbidden, and a message carrying `Connection`, `Keep-Alive`, `Transfer-Encoding`, `Upgrade` or `Proxy-Connection` must be treated as malformed. The single carve-out is `TE`, permitted only with the exact value `trailers`. Connection management moved into the framing layer (SETTINGS, GOAWAY, stream priorities), and protocol upgrade is handled by extended CONNECT rather than the `Upgrade` field. This makes translation the interesting case. A gateway terminating HTTP/2 and forwarding to an HTTP/1.1 back end must *generate* appropriate hop fields for the upstream connection and must refuse any smuggled `transfer-encoding` or `connection` field that arrived over HTTP/2. A gateway going the other way must strip them. Getting this wrong is the h2-downgrade smuggling family. ## Operationally When a header "disappears" between the client and the application, the checklist is: is it named in `Connection`? Is it in the fixed hop-by-hop set? Is a proxy configured with an explicit allow-list? Reverse proxies typically drop hop-by-hop fields silently and by design, which is exactly the confusion that produces the interview question. The design lesson: any field you invent for your own infrastructure must be either genuinely end-to-end, or explicitly declared hop-scoped and stripped at the boundary — never accidentally relayed because the proxy copies everything it receives.
- A WebSocket handshake fails behind a reverse proxy with 400 or 200 instead of 101. What is the likely cause?The proxy stripped the hop-by-hop Upgrade and Connection fields, as it is required to do by default, so the upstream never saw an upgrade request. The fix is to configure that route explicitly to forward Upgrade and set Connection: upgrade on the upstream connection, which is a deliberate per-route decision rather than blanket header copying.
- Why does HTTP/2 treat a Transfer-Encoding field as a malformed message rather than ignoring it?Because HTTP/2 frames bodies itself with DATA frames and END_STREAM, so the field carries no meaning and can only originate from a broken sender or an attacker probing a downgrade path. Ignoring it would let it survive translation to an HTTP/1.1 back end and desynchronise that hop, so the specification requires rejection.
saying these in an interview costs you the question
- Copying every inbound header to the upstream request in a custom proxy
- Thinking Connection only ever means close or keep-alive
- Forwarding Proxy-Authorization to the origin server
- Assuming a stripped header is a proxy bug rather than required behaviour
- Believing hop-by-hop fields still apply in HTTP/2 because the semantics are otherwise unchanged