skip to content

questions

4

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

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

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