skip to content

questions

18

HTTP/1.1 made the Host request header mandatory on every request. What problem does Host solve, and what is a server required to do when a request arrives with no Host header, or with two Host headers?

level: juniorimportance: must knowfreq 55%

answer

  1. HTTP/1.0 request line had path only
  2. One IP, many sites = name-based vhosting
  3. Exactly one Host, else 400
  4. Duplicate Host → smuggling/cache poisoning
  5. h2/h3: :authority replaces it

basics

~20 s

Host names the target site, so one IP and port can serve many domains (name-based virtual hosting). Every HTTP/1.1 request must carry exactly one Host; a missing or duplicated Host must be answered with 400 Bad Request.

solid answer

~50 s

In HTTP/1.0 the request line carried only the path, so a server listening on one IP:port could not tell which hostname the client typed. HTTP/1.1 fixed that by requiring a `Host` header carrying the authority (host plus optional non-default port) from the target URI. That single header is what makes **name-based virtual hosting** possible: nginx `server_name`, Apache `ServerName`, and Kubernetes ingress host rules all route on it. The rule is strict: a server **must** respond `400 Bad Request` to an HTTP/1.1 request that has no Host field, or that has more than one Host field, or whose Host value is invalid. The reason is ambiguity — if two components in a chain (CDN, reverse proxy, origin) resolve duplicate Host headers differently, you get request smuggling and cache poisoning. Rejecting outright removes the ambiguity. In HTTP/2 and HTTP/3 the same information moves to the `:authority` pseudo-header.

code

http · 4 lines
http
GET /cart HTTP/1.1
Host: shop.example.com
User-Agent: curl/8.4.0
Accept: */*

go deeper

for a junior

Be able to say what Host contains, that it enables many sites on one IP, and that HTTP/1.1 requires it.

for a middle

Add the exact failure behaviour (400 on missing, duplicate, or malformed Host) and the mapping to :authority in HTTP/2 and HTTP/3.

for a senior

Explain why duplicates are a security problem (cache poisoning, request smuggling), how default-server fallthrough bites health checks, and how absolute-form interacts with Host.

for a principal

Frame Host as the routing key of the whole edge: which tier is authoritative for hostname validation, how unknown hosts are handled (default server, 421), and the blast radius of letting the client-supplied name flow into cache keys and generated URLs.

## What Host is `Host` is a request header whose value is the *authority* component of the URL the client is trying to reach: a hostname, optionally followed by a colon and a port when the port is not the default for the scheme. For `https://shop.example.com/cart` the client sends `Host: shop.example.com`. For `http://api.example.com:8080/v1/ping` it sends `Host: api.example.com:8080`. ## Why it had to be invented An HTTP/1.0 request line looks like `GET /index.html HTTP/1.0`. It contains a path and nothing else. The only thing that told the server which site was wanted was the TCP connection itself — the destination IP address and port. That worked while every website had its own IP address. It stopped working as the web grew. IPv4 addresses are scarce and expensive, and hosting providers wanted to put hundreds of sites on one machine behind one address. A server receiving `GET /index.html` on port 80 has no way to know whether the visitor typed `alice.example.com` or `bob.example.com`; both resolve to the same address, and both produce a byte-identical request. HTTP/1.1 solved this by requiring the client to state the hostname explicitly. The specification wording (RFC 9110/9112, previously RFC 2616) is that a client **MUST** send a `Host` header field in every HTTP/1.1 request message, and it must be sent before the message body. ## Name-based virtual hosting Once Host exists, one listening socket can serve many sites. The server keeps a table of hostnames to configurations and dispatches on the Host value: - **nginx** matches `server_name` inside `server` blocks; a request whose Host matches nothing falls through to the *default server* for that listen address. - **Apache httpd** matches `ServerName`/`ServerAlias` inside `VirtualHost`; unmatched requests go to the first defined vhost. - **Kubernetes ingress controllers, API gateways, and CDNs** all express routing rules as host plus path. The alternative, *IP-based virtual hosting*, gives each site its own address and needs no Host header — it is still occasionally used but wastes addresses. ## The error rules: missing, duplicate, invalid The spec is unusually blunt here. A server **must** respond with `400 Bad Request` when an HTTP/1.1 request: - contains no `Host` field, - contains more than one `Host` field, or - contains a `Host` field with an invalid field value (bad syntax, disallowed characters). The motivation is security, not pedantry. If a request carries two Host headers, every component in the chain has to guess which one counts. A CDN might route on the first and an origin cache might key on the second, so an attacker can make the cache store a response for host A under a key for host B — classic cache poisoning — or split a request across a proxy boundary (request smuggling). Making duplicates a hard error removes the guess. ## Request-target forms and how Host interacts Most requests use *origin-form*: `GET /cart HTTP/1.1` plus a Host header. Two other forms exist: - **absolute-form** — `GET http://example.com/cart HTTP/1.1` — used by clients talking to a forward proxy. Host must still be present for compatibility, but the authority inside the request line wins; the server ignores Host. - **authority-form** — `CONNECT example.com:443 HTTP/1.1` — used to establish a tunnel; the authority is the request target itself. ## What replaced it in HTTP/2 and HTTP/3 HTTP/2 and HTTP/3 do not use a Host header as the primary carrier. They use the `:authority` pseudo-header field. A client may also send Host, but if both appear they must agree; an intermediary translating HTTP/2 down to HTTP/1.1 generates the Host header from `:authority`. Conceptually nothing changed — the authority is still mandatory metadata — only the encoding did. ## Practical consequences you will hit - Testing a vhost before DNS is switched: `curl -H 'Host: shop.example.com' http://1.2.3.4/` or, so TLS also works, `curl --resolve shop.example.com:443:1.2.3.4`. - Load-balancer health checks that dial the IP directly send `Host: 1.2.3.4`, match no vhost, and land on the default server — a frequent cause of "the LB says the pod is unhealthy but curl from my laptop works". - Anything that generates absolute links from the request Host inherits whatever the client sent, which is an injection vector unless the accepted hostnames are constrained.

  • A request arrives in absolute form, `GET http://a.example.com/ HTTP/1.1`, but also carries `Host: b.example.com`. Which one wins?
    The authority in the request line wins; the server ignores the Host header in that case. Absolute-form is what clients send to forward proxies, and Host is only still required so that HTTP/1.0-era intermediaries do not break. A server that routed on Host here would disagree with a proxy that routed on the request line, which is exactly the kind of mismatch that enables smuggling.
  • Why is duplicating the Host header treated as a hard error rather than just taking the first value?
    Because different components in a request chain may pick different values. A CDN might route on the first Host while the origin caches on the second, letting an attacker store a response for one site under another site's cache key, or desynchronise a proxy and origin so a smuggled request is attributed to another user's connection. A mandatory 400 removes the ambiguity at the first hop.
  • A load balancer health check gets a 404 while browser traffic to the same server works. What would you check first?
    The Host header the health check sends. Checks that dial the pod IP directly send the IP as Host, match no `server_name`/`ServerName`, and fall through to the default virtual host, which often has no such route. Configuring the check to send the real hostname, or adding an explicit default server for health endpoints, fixes it.

An apartment building with one street address: the street number gets the mail to the building (the IP), but without an apartment number on the envelope (Host) the mail room cannot decide which tenant gets it.

saying these in an interview costs you the question

  • Saying Host contains the full URL including path and scheme — it is only the authority, host plus optional port.
  • Believing the server learns the hostname from DNS or from the TCP connection; the server only sees an IP and port and must be told the name.
  • Claiming a missing Host is fine because the server can pick a default — HTTP/1.1 requires a 400.
  • Thinking HTTP/2 dropped the concept; it moved to the :authority pseudo-header.
  • Assuming Host includes the port always — the default port for the scheme is omitted.

context

open as a page

In HTTP/1.1, how does the receiver of a message know where the body ends, and what exactly does the Content-Length header field count?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Content-Length states the body size in octets, so the receiver reads exactly that many bytes and stops. If it is absent, HTTP/1.1 can use Transfer-Encoding: chunked, where each chunk announces its own size and a zero-size chunk ends the body.

open as a page

Walk through the wire format of an HTTP/1.1 message that uses Transfer-Encoding: chunked: how a chunk is written, how the body is terminated, and what trailer fields are for.

level: middleimportance: must knowfreq 52%

basics

~20 s

Each chunk is its size in hexadecimal, CRLF, that many octets, CRLF. A chunk of size 0 ends the body. After the terminal chunk, optional trailer fields may appear (metadata such as a checksum computed only after streaming), then a final empty line.

open as a page

Compare the HTTP Forwarded header defined by RFC 7239 with X-Forwarded-For and X-Forwarded-Proto, and explain how a service behind several proxies should determine the real client IP.

level: seniorimportance: must knowfreq 55%

basics

~20 s

Forwarded is the standard single header carrying for, proto, host and by parameters; X-Forwarded-For and X-Forwarded-Proto are the de-facto split equivalents. All are client-spoofable, so trust only entries appended by proxies you control: count hops from the rightmost end, never take the leftmost value blindly.

open as a page

Describe how a correlation or request ID header (for example X-Request-Id or the W3C traceparent header) flows through a system of services, and what each service is responsible for doing with it.

level: juniorimportance: should knowfreq 50%

basics

~20 s

The edge generates an ID if the incoming request has none, puts it in the logging context, echoes it on the response, and forwards it on every downstream call. Each service reuses the received value rather than minting a new one, so one ID ties all logs for a request together.

open as a page

What does HTTP status 431 Request Header Fields Too Large mean, when is a server supposed to send it, and why do many real servers send 400 Bad Request in the same situation instead?

level: juniorimportance: should knowfreq 38%

basics

~20 s

431 means the request's header fields are too large — either one field or the whole set — so the server refused to process it. It is the correct code, but many servers (and proxies) reject oversized headers with 400 Bad Request or just close the connection, because the failure happens mid-parse.

open as a page

HTTP fields are commonly grouped into categories such as control data, representation metadata, request context and response context. What does each group mean, and why is the distinction useful when you read or design a message?

level: juniorimportance: should knowfreq 44%

basics

~20 s

Control data steers how the message is handled (Host, Cache-Control, Range, Date). Representation metadata describes the bytes being carried (Content-Type, Content-Encoding, Content-Language) and validators identify their version (ETag, Last-Modified). Context fields describe the sender or the resource (User-Agent, Referer, Server, Allow). Grouping tells you what a field applies to: the message, the representation, or the party.

open as a page

Why does RFC 6648 deprecate the X- prefix on new HTTP header field names, and how would you name and design a custom header today?

level: middleimportance: should knowfreq 42%

basics

~20 s

The X- prefix was meant to mark experiments, but successful ones get standardised and then you are stuck with either a permanent X- name or two names for one thing. RFC 6648 says pick the real name immediately, ideally vendor-scoped, and never treat X- as meaningful.

open as a page

HTTP/2 and HTTP/3 replace the Host header with an `:authority` pseudo-header. What are pseudo-headers, what rules govern `:authority`, and how does a proxy translate between `:authority` and Host when downgrading to HTTP/1.1?

level: middleimportance: should knowfreq 33%

basics

~20 s

Pseudo-headers are colon-prefixed fields (:method, :scheme, :path, :authority) that encode the request line in HTTP/2 and HTTP/3. :authority carries what Host carried. They must come before regular fields; if Host also appears it must match, and a proxy downgrading to HTTP/1.1 generates Host from :authority.

open as a page

When one IP address serves many HTTPS sites, what job does the TLS SNI extension do compared with the HTTP Host header, and what should a server do when the two names disagree?

level: middleimportance: should knowfreq 42%

basics

~20 s

SNI is sent in the TLS ClientHello, before encryption, so the server can pick the right certificate. Host is sent inside the encrypted HTTP request and picks the application/virtual host. They should match; a mismatch means the server is not authoritative and can answer 421.

open as a page

Roughly how large a request header section do common HTTP servers accept by default, which settings control it (for example nginx's large_client_header_buffers and Tomcat's maxHttpHeaderSize), and what should you weigh before raising the limit?

level: middleimportance: should knowfreq 36%

basics

~20 s

Most servers cap headers around 8 KB by default: nginx large_client_header_buffers 4 8k, Tomcat maxHttpHeaderSize 8192, Apache LimitRequestFieldSize 8190; Node.js allows 16 KB. Raise it only knowingly — the buffer is per connection, so bigger limits multiply memory and DoS exposure — and raise it on every tier.

open as a page

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.

level: middleimportance: should knowfreq 40%

basics

~20 s

Field 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.

open as a page

A custom request header your client sets never reaches the application once traffic goes through a CDN and load balancer, though it works when you call the service directly. How do you diagnose and fix that?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Bisect the path hop by hop with an echo endpoint. Common causes: the CDN or proxy forwards only allowlisted headers, the name contains an underscore that nginx drops, the field is listed in Connection so it is hop-by-hop, header size limits, or a browser CORS preflight rejecting it. Fix at the hop that drops it.

open as a page

An application builds absolute URLs — password-reset links, redirects, canonical tags — from the HTTP Host header of the incoming request. What can go wrong, and how would you make host handling safe at the edge and in the application?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Host is attacker-controlled input. If a catch-all virtual host accepts any Host, an attacker can make the app emit links to their domain — password-reset tokens sent to evil.com — or poison shared caches. Fix: allowlist hostnames at the edge, and build URLs from configured values, not from the request.

open as a page

An HTTP/1.1 request arrives carrying both a Content-Length header field and Transfer-Encoding: chunked. What do the framing rules say a recipient must do, and why does disagreement between a front-end proxy and a back-end server on this point create a security problem?

level: seniorimportance: should knowfreq 44%

basics

~20 s

A message must never carry both. Transfer-Encoding wins over Content-Length, but the message is treated as malformed: a server should reject it, and a proxy must not forward both fields. If a proxy honours one field and the back end honours the other, they disagree on where the request ends, and leftover bytes become a forged request prefixed onto the next user's request. That is request smuggling.

open as a page

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?

level: seniorimportance: should knowfreq 38%

basics

~20 s

End-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.

open as a page

You are setting a request header size budget for a platform with a CDN, a load balancer, an ingress tier and dozens of services. How would you choose the number, decide what is allowed to consume it, and stop it from being exceeded again a year later?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Pick one number the whole chain honours (commonly 16–32 KB), sized from measured p99 plus proxy-added headers and checked against limit times peak concurrency for memory. Make outer tiers no more permissive than inner ones, allocate the budget explicitly, and monitor p99 against it.

open as a page