AnyLearn
All lessons
Computer Scienceintermediate

DNS, HTTP, and the Move to QUIC

Before a byte of HTTP moves, a name has to become an address. Follow the full resolution path from stub resolver to authoritative server, see how TTLs govern change, then trace head-of-line blocking from HTTP/1.1 through HTTP/2's TCP problem to HTTP/3 running on QUIC over UDP.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 12

The lookup nobody counts

Every connection in the previous lessons started from an IP address. Nothing in a browser does. It starts from a name, and turning that name into an address is a distributed database query that happens before the first packet of your actual request.

The Domain Name System, defined in RFC 1034 and RFC 1035, is a hierarchical delegation tree read right to left. In www.example.com the trailing empty label is the root, then com, then example, then www. Each level tells you nothing except which servers know about the level below.

That design choice is the whole point. No single machine holds the mapping. The root knows only which servers run com. The com servers know only which nameservers a domain delegated to. Only those final nameservers hold the actual address record.

The cost is that a cold lookup can take several round trips before your connection even begins. The benefit is that a domain owner changes their records without asking anyone.

Full lesson text

All 12 steps on one page, for reading, reference, and search.

Show

1. The lookup nobody counts

Every connection in the previous lessons started from an IP address. Nothing in a browser does. It starts from a name, and turning that name into an address is a distributed database query that happens before the first packet of your actual request.

The Domain Name System, defined in RFC 1034 and RFC 1035, is a hierarchical delegation tree read right to left. In www.example.com the trailing empty label is the root, then com, then example, then www. Each level tells you nothing except which servers know about the level below.

That design choice is the whole point. No single machine holds the mapping. The root knows only which servers run com. The com servers know only which nameservers a domain delegated to. Only those final nameservers hold the actual address record.

The cost is that a cold lookup can take several round trips before your connection even begins. The benefit is that a domain owner changes their records without asking anyone.

2. Stub, recursive, authoritative

Three distinct roles get lumped together as "DNS", and confusing them makes every debugging session harder.

  • The stub resolver lives in your operating system. It is deliberately dumb: it sends one query to a configured server and waits for a final answer. It cannot follow delegations.
  • The recursive resolver does the work. Your ISP runs one, and so do public services like 8.8.8.8 and 1.1.1.1. It walks the tree, caches everything it learns, and returns a complete answer.
  • The authoritative server holds the actual zone data for a domain and answers only for the names it owns. It never chases referrals on your behalf.

A reply carries an authority flag that tells you which kind you got. This is why a record can be correct at the registrar, correct at the authoritative server, and still wrong for a user: their recursive resolver is serving a cached copy, and no amount of fixing the origin changes that until the cache expires.

3. Walking the delegation by hand

You can perform a recursive resolver's job manually, one referral at a time. Start by asking a root server which nameservers run a top-level domain:

> nslookup -type=NS com. a.root-servers.net
Server:  a.root-servers.net
Address: 198.41.0.4

com     nameserver = l.gtld-servers.net
com     nameserver = j.gtld-servers.net
com     nameserver = h.gtld-servers.net
com     nameserver = d.gtld-servers.net

The root did not answer the question you care about. It handed back a referral. You would then ask a gtld-server which nameservers a domain delegated to, and finally ask those for the record itself:

> nslookup -type=NS anylearn.cc
anylearn.cc     nameserver = arch.ns.cloudflare.com
anylearn.cc     nameserver = jasmine.ns.cloudflare.com

Notice that the referral includes glue records, the IP addresses of those nameservers. Without glue you could not resolve arch.ns.cloudflare.com without first resolving arch.ns.cloudflare.com, and the whole system would deadlock on its own bootstrap.

4. One cold resolution

With an empty cache, a single name costs the recursive resolver three referrals before it can answer.

sequenceDiagram
  participant A as Stub resolver
  participant R as Recursive resolver
  participant Z as Root server
  participant T as TLD server
  participant N as Authoritative server
  A->>R: What is the address of www.example.com
  R->>Z: Same question
  Z-->>R: Referral to the com servers
  R->>T: Same question
  T-->>R: Referral to example.com nameservers
  R->>N: Same question
  N-->>R: Address record plus TTL
  R-->>A: Final answer, cached on the way out

5. TTL: the only control you have

Nobody would tolerate three referrals per lookup, so every record carries a time to live and resolvers cache aggressively. Query a name twice and watch the number fall:

> nslookup -type=A -debug example.com 8.8.8.8
    ttl = 206 (3 mins 26 secs)
    internet address = 104.20.23.154
    ttl = 206 (3 mins 26 secs)
    internet address = 172.66.147.243

That 206 is the remainder of the resolver's countdown, not the record's configured value. When it hits zero the resolver goes back to the authoritative server.

Failures are cached too. RFC 2308 defines negative caching: the TTL for a nonexistent name comes from the minimum of the zone's SOA MINIMUM field and the SOA record's own TTL, and the RFC advises resolvers to cap negative caching at one to three hours because longer values proved harmful in practice.

The operational consequence is a rule worth internalising. TTL is your rollback speed. If a record has a 24-hour TTL and you point it at a broken server, you own that mistake for 24 hours. Lower the TTL a day before a migration, not during it.

6. Thirteen roots, two thousand machines

The root of the tree is famously limited to 13 names, from a.root-servers.net to m.root-servers.net. The constraint was a packet, not a policy: classic DNS over UDP capped a message at 512 bytes, and 13 nameserver records with their IPv4 glue was the most that fit without fragmenting.

That number is now almost decorative. Those 13 names are announced by anycast, meaning many physically separate machines announce the identical IP prefix from different locations, and BGP routes each query to whichever instance is nearest in routing terms. The Root Server Technical Operations Association reported 2,001 operational instances on 20 July 2026, run by 12 independent organisations.

Anycast is the same trick that keeps 1.1.1.1 and 8.8.8.8 fast worldwide, and it works because DNS queries are single-packet request and reply exchanges over UDP. There is no connection state to lose if routing shifts a query to a different instance mid-flight, which is exactly why anycast is straightforward for DNS and considerably harder for long-lived TCP.

7. HTTP/1.1 and its ordering trap

With an address in hand and a TCP connection open, HTTP/1.1 as specified in RFC 9112 sends a text request and reads a text response. One at a time.

HTTP/1.1 does allow pipelining: send several requests without waiting. The catch is that responses must come back in request order. A slow first response blocks every response queued behind it, even if they are all ready. That is head-of-line blocking at the application layer, and it is why no major browser ever shipped pipelining on by default; Firefox removed the support entirely.

With pipelining unusable, the workaround was parallel connections, typically six per origin. Each one pays its own handshake and its own slow start, and six connections competing for one bottleneck also confuses congestion control.

That limit then shaped a decade of web practice. Domain sharding split assets across img1.example.com and img2.example.com to buy six more connections each. Sprite sheets bundled dozens of icons into one image. Concatenated bundles turned many small files into one big file. All of it was working around a protocol constraint, and all of it became counterproductive the moment that constraint was lifted.

8. HTTP/2: one connection, many streams

HTTP/2, specified in RFC 9113, keeps HTTP semantics identical and rewrites the wire format. Three changes carry the weight.

First, framing is binary. A message becomes a sequence of typed frames such as HEADERS and DATA, each tagged with a stream identifier. Parsing is unambiguous and cheap.

Second, streams are multiplexed. Many concurrent requests share one TCP connection, their frames interleaved, and responses arrive in whatever order they are ready. The application-layer ordering trap is gone, and with it the reason for domain sharding and sprite sheets.

Third, headers are compressed with HPACK, which maintains a shared table of previously sent header fields. In HTTP/1.1 every request repeats the same several hundred bytes of cookies and user agent. HPACK reduces the repeat to a table index.

One connection also means one handshake, one TLS negotiation, and one congestion window that stays warm instead of six that keep restarting. That last point matters more than the header savings on most real pages.

9. The problem HTTP/2 could not fix

Multiplexing solved head-of-line blocking in HTTP. It could not solve it in TCP, because TCP has no idea the streams exist.

TCP delivers one ordered byte stream. When a segment is lost, the kernel holds every later byte in its receive buffer until the retransmission arrives, because handing them up would violate the ordering guarantee. Those held bytes may belong to twenty unrelated streams that are perfectly complete.

RFC 9114 states it directly: "because the parallel nature of HTTP/2's multiplexing is not visible to TCP's loss recovery mechanisms, a lost or reordered packet causes all active transactions to experience a stall regardless of whether that transaction was impacted by the lost packet."

The irony is exact. HTTP/1.1 with six connections had six independent TCP streams, so one loss stalled roughly one sixth of the page. HTTP/2 with one connection concentrated everything, so one loss stalls all of it. On a clean network HTTP/2 wins comfortably; on a lossy mobile link the comparison narrows and can invert.

Fixing this requires a transport that knows about streams. TCP cannot be changed, because middleboxes everywhere would reject anything unfamiliar.

10. QUIC: streams that fail independently

QUIC, specified in RFC 9000, is a transport built on UDP. UDP is not the point; it is the delivery vehicle, chosen because it is the only thing every network already forwards and no middlebox tries to interpret. Everything TCP does is reimplemented above it, in userspace, where it can actually be changed.

The defining property is that streams are a transport concept. RFC 9000 describes streams that "can be created by either endpoint, can concurrently send data interleaved with other streams, and can be canceled." QUIC tracks loss per packet and delivery order per stream, so a lost packet blocks only the streams whose bytes were in it. Everything else is delivered immediately. That is the HTTP/2 problem removed at the layer where it actually lives.

Two more consequences follow. TLS 1.3 is integrated into the handshake rather than layered on top, so the cryptographic setup and the transport setup share round trips. And a connection is identified by a connection ID rather than by the four-tuple of addresses and ports, so it survives a change of network path. Walk out of Wi-Fi onto cellular and your address changes, but the connection ID does not, and the session continues.

11. HTTP/3 and the 0-RTT bargain

HTTP/3, published as RFC 9114 in June 2022, is HTTP/2's model mapped onto QUIC streams. Same methods, same status codes, same header semantics. Each request and response pair gets its own QUIC stream, and header compression moves from HPACK to QPACK, which is redesigned so that out-of-order stream delivery cannot corrupt the shared compression table.

The headline feature is 0-RTT resumption. A client that has connected before can encrypt a request with cached parameters and send it in its very first packet, so the request is already at the server when the handshake completes.

The bargain is spelled out in the specification. 0-RTT data has no replay protection: an attacker who captures that first flight can send it again later, and the server cannot tell the difference. So 0-RTT is only safe for idempotent requests, and a server that receives something it will not process early responds with 425 Too Early to force a full handshake and a retry.

Gotcha: "idempotent" here is a property of what the request does, not of its method. A GET that increments a counter is not safe to replay just because it is a GET.

12. Where this actually lands

All three versions are in production simultaneously, and none is universally best.

HTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC over UDP
FormatTextBinary framesBinary frames
ConcurrencyParallel connectionsStreams on one connectionStreams on one connection
App-layer HOL blockingYesNoNo
Transport HOL blockingPer connectionWhole connectionPer stream only
Survives a network changeNoNoYes, via connection ID

Cloudflare's 2025 Radar Year in Review reported the split across its own traffic as 50 percent HTTP/2, 29 percent HTTP/1.x, and 21 percent HTTP/3, with all three roughly flat over the year.

HTTP/3 is not a free upgrade either. Zhang and colleagues, in "QUIC is not Quick Enough over Fast Internet" at the ACM Web Conference 2024, measured up to a 45.2 percent reduction in data rate against TCP with HTTP/2 on high-bandwidth paths, and traced it to receiver-side processing cost rather than to the protocol design: too many small packets and acknowledgements handled in userspace. The gap widened as bandwidth rose.

The general lesson holds beyond QUIC. Moving a protocol out of the kernel buys evolvability and costs CPU per packet, and which side wins depends on the network you are actually on.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. You fix a wrong A record at your authoritative nameserver, but some users still reach the old server for hours. Why?
    • Their stub resolvers walk the delegation tree independently and found a stale referral.
    • Their recursive resolvers are serving the previous answer until its TTL counts down to zero.
    • The root servers replicate zone changes on a fixed multi-hour schedule.
    • Negative caching prevents the new record from being accepted until the SOA expires.
  2. Why was the number of DNS root server names limited to 13?
    • ICANN allocated one operator per founding member organisation.
    • Thirteen was the maximum number of anycast prefixes BGP could carry at the time.
    • Thirteen NS records with their IPv4 glue was the most that fit in a 512-byte UDP DNS message.
    • The root zone signature algorithm supports at most 13 key holders.
  3. A single packet is lost on an HTTP/2 connection carrying twenty concurrent requests. What happens?
    • Only the stream whose bytes were in that packet stalls; the other nineteen continue.
    • All twenty streams stall, because TCP must deliver bytes in order and cannot see the streams.
    • HTTP/2 retransmits at the frame level, so nothing stalls.
    • The connection is closed and the browser opens six parallel connections instead.
  4. What does QUIC's connection ID make possible that a TCP connection cannot do?
    • Sending application data before the transport handshake finishes.
    • Compressing headers across multiple concurrent requests.
    • Delivering streams independently when a packet is lost.
    • Continuing the same session after the client's IP address changes.
  5. A team wants to enable HTTP/3 0-RTT for all requests to speed up repeat visits. What is the concrete risk?
    • 0-RTT data has no replay protection, so a captured first flight can be replayed against the server.
    • 0-RTT disables TLS entirely for the first request of a resumed session.
    • 0-RTT forces the connection back to HTTP/2 if the server does not respond in one round trip.
    • 0-RTT requires the client to reuse the same IP address, breaking mobile clients.

Related lessons

Computer Science
intermediate

TCP: Reliability, Windows, and Congestion

TCP turns a lossy packet service into an ordered byte stream, and almost every performance surprise on the internet comes from how it does that. Work through the handshake, cumulative ACKs, retransmission timers, the two windows, slow start and AIMD, CUBIC versus BBR, bufferbloat, and when to reach for UDP instead.

13 steps·~20 min
Programming
beginner

HTTP Status Codes: Understanding Each Class and When to Use Them

HTTP status codes are essential for effective communication between clients and servers on the web. This lesson breaks down each class of status codes (1xx, 2xx, 3xx, 4xx, 5xx), explains their meaning, and provides practical guidance on when to use them in your applications.

10 steps·~15 min
Programming
advanced

Pods, Services, and the Label That Joins Them

The object types look like an arbitrary vocabulary until you notice they are layers of controllers, each reconciling the one below. This lesson works up from the pod, explains why a Service can point at something whose address changes constantly, and shows that the whole system is joined by one mechanism: a label match.

8 steps·~12 min
Programming
advanced

Networking and Storage: Connecting an Isolated Thing

Isolation is the point, and a container that can reach nothing and keep nothing is useless. This lesson covers how a network namespace is wired to the outside, why published ports and container-to-container traffic work completely differently, and where data has to live given that the writable layer disappears.

8 steps·~12 min