Type a URL into a browser and press enter. Before any HTML shows up, before any request for a webpage even makes sense, two things have to happen that have nothing to do with the web itself: a name has to be turned into an address, and two machines that have never spoken before have to establish a reliable channel to talk over.

This article covers both of those steps in order: DNS resolution, and TCP connection setup. Nothing here is specific to HTTP, browsers, or any particular product. It's the same handshake whether you're loading a webpage, connecting to a database, or SSHing into a server. By the end, you should be able to trace, step by step, everything that happens before a single application-level byte is exchanged.

The Problem: Names Aren't Addresses

example.com means nothing to a router. Routers move data based on numeric addresses, because those addresses can be organized hierarchically and used to make fast forwarding decisions. A human-readable name carries no routing information at all. Somewhere between you typing a name and your machine sending a packet, that name has to become an IP address.

This is the job of DNS, the Domain Name System. It's easy to think of DNS as "the internet's phone book," and that's not wrong, but the more useful way to think about it is as a distributed, cached lookup system, because both of those properties, distribution and caching, explain almost every interesting behavior DNS has, including its failure modes.

How DNS Resolution Actually Works

DNS is organized as a hierarchy, and that hierarchy exists so that no single server has to know every domain name in existence.

When your machine needs to resolve example.com, it doesn't usually go straight to the source. It asks a recursive resolver, typically operated by your ISP or a public resolver you've configured, to do the work on its behalf. Think of the recursive resolver as a librarian who doesn't know the answer either, but knows exactly who to ask.

The recursive resolver walks the hierarchy from the top down:

  1. It asks a root server where to find information about .com domains. The root server doesn't know about example.com specifically, but it knows which servers are responsible for the .com top-level domain (TLD).
  2. It asks the TLD server for .com where to find example.com specifically. The TLD server doesn't have the answer either, but it knows which authoritative server is responsible for that domain.
  3. It asks the authoritative server for example.com, which finally returns the actual IP address.
sequenceDiagram
    participant Client
    participant Recursive as Recursive Resolver
    participant Root as Root Server
    participant TLD as .com TLD Server
    participant Auth as Authoritative Server (example.com)

    Client->>Recursive: Where is example.com?
    Recursive->>Root: Where is example.com?
    Root-->>Recursive: Ask the .com TLD servers
    Recursive->>TLD: Where is example.com?
    TLD-->>Recursive: Ask this authoritative server
    Recursive->>Auth: Where is example.com?
    Auth-->>Recursive: It's at 93.184.216.34
    Recursive-->>Client: 93.184.216.34

Each step in that chain is answering a narrower question than the last. Root servers know about TLDs. TLD servers know about authoritative servers. Authoritative servers know actual records. Nobody needs to hold the entire namespace in memory, which is exactly why this design scales to a namespace with no practical upper bound.

Why Caching Exists, and Why It Bites You

Every one of those answers comes with a TTL, a time-to-live, that tells the recursive resolver how long it's allowed to reuse that answer before asking again. This exists for an obvious reason: walking that entire chain for every single lookup would be wasteful, and most domains don't change their IP address every few seconds.

The tradeoff is staleness. If a domain's IP address changes and a resolver is still holding a cached answer from before the change, it will confidently hand out the wrong address until the TTL expires. This is why DNS changes don't take effect instantly. It's also a common, invisible cause of "the site is slow" reports that have nothing to do with the site: if a resolver somewhere along the chain is slow to answer, or if a cached record just expired and a full resolution chain has to run again, the delay happens before the browser has even sent a single request. From the user's perspective, the page is just slow. From an engineer's perspective, the connection hadn't even started yet.

What an IP Address Actually Identifies

An IP address identifies a network interface, not a person, not a browser tab, and not even always a single physical machine. That distinction matters because it explains two things that otherwise look like exceptions: NAT, and IPv6.

IPv4 addresses are 32 bits, which gives roughly 4.3 billion possible addresses. That sounds like a lot until you remember how many devices exist. Long before every address was exhausted, engineers needed a way to let many devices share a small number of public addresses. That's NAT (Network Address Translation): a router keeps a table mapping internal, private addresses to a shared public address plus a port number, translating traffic in both directions. It's why your laptop's address at home isn't reachable directly from the internet, and why a whole home network can share one public IP.

IPv6 exists to solve the underlying scarcity problem directly rather than working around it. Its addresses are 128 bits, which produces a number of possible addresses large enough that NAT stops being a practical necessity for address conservation (though it can still be used for other reasons). You don't need to memorize IPv6 address notation to understand the web; you just need to know it exists because 32 bits eventually wasn't enough, and NAT was always a workaround, not a permanent architecture.

From an Address to a Connection

Once your machine has an IP address, it doesn't have a direct wire to the destination. Packets travel through a chain of routers, each one forwarding the packet toward a destination it's slightly closer to, based on routing tables built from information routers exchange with each other. You don't need the full picture of internet routing to understand what happens next; you just need to know that "sending a packet" means "handing it to the first router, which hands it to the next, and so on," not "opening a direct pipe to the destination."

What you do need to understand well is what happens once packets can reach the destination: how a connection actually gets established.

The TCP Three-Way Handshake

Most connections on the web use TCP (Transmission Control Protocol). Before either side sends any real data, TCP requires a specific three-step exchange, commonly called the three-way handshake:

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: SYN (seq=x)
    Server-->>Client: SYN-ACK (seq=y, ack=x+1)
    Client->>Server: ACK (ack=y+1)
    Note over Client,Server: Connection established
  1. The client sends a SYN (synchronize) packet with an initial sequence number.
  2. The server responds with SYN-ACK: its own sequence number, plus an acknowledgment of the client's.
  3. The client responds with ACK, acknowledging the server's sequence number.

This isn't procedural ceremony. It exists to solve a real problem: neither side can safely assume the other is ready to send and receive data until it has proof in both directions. A single message from client to server only proves the client can send; it says nothing about whether the server received it or is willing to talk back. The handshake requires both sides to demonstrate, in both directions, that they can send and that they've received, before either commits any real application state to the connection. Skip that, and you risk building on top of a connection that only works one way, or doesn't exist at all on the other end.

The sequence numbers exchanged during the handshake aren't arbitrary either. They're the starting point for ordered delivery, which is the next piece of what TCP guarantees.

Why TCP, and What It Actually Guarantees

TCP exists because raw IP packet delivery makes almost no promises. Packets can arrive out of order, get duplicated, or simply never arrive at all. Most applications can't tolerate that, so TCP adds a layer of guarantees on top:

  • Ordered delivery: every byte sent is tagged with a sequence number, so the receiving side can reassemble data in the correct order even if packets arrive out of sequence.
  • Retransmission: if the sender doesn't receive an acknowledgment for data within an expected window, it resends it, on the assumption that the packet was lost.
  • Flow control: the receiver advertises how much data it's currently able to buffer, so a fast sender can't overwhelm a slow receiver.
  • Congestion control: the sender paces how much data it puts on the wire based on observed loss and delay, because the network itself (not just the receiver) has a limited capacity to carry traffic.

None of these are free. They cost overhead: acknowledgments have to be sent, retransmissions add latency, and congestion control means a new connection doesn't blast data at full speed immediately. That overhead is the price of reliability.

It's worth briefly contrasting this with UDP (User Datagram Protocol), which offers none of these guarantees. UDP just sends datagrams and does not track whether they arrived, in what order, or at all. That sounds strictly worse, but it means UDP has far less overhead and no built-in latency cost from acknowledgments or retransmission logic. That tradeoff, reliability versus overhead, seems like a minor footnote here, but it's the exact tension that reshapes how HTTP itself is transported later in this series.

Closing the Connection

TCP connections don't just stop; they're torn down explicitly, using a FIN/ACK exchange similar in spirit to the handshake that opened them. Either side can initiate teardown by sending a FIN, which the other side acknowledges, and the process repeats in the other direction until both sides have confirmed there's nothing left to send. This matters less for understanding day-to-day behavior than the handshake does, but it's worth knowing it exists: a connection is a negotiated state on both ends, not just something that quietly evaporates when data stops flowing.

What You Should Be Able to Trace Now

At this point, you should be able to walk through, in order: how a name becomes an address through a chain of increasingly specific DNS servers, why that chain is cached and what breaks when the cache is stale, what an IP address actually identifies and why NAT and IPv6 both exist, and how a TCP connection gets established through a handshake that proves both sides can send and receive before any real data moves.

None of that has involved encryption, and none of it has involved HTTP. What you have, at this point, is a reliable, ordered, bidirectional channel between two machines, and nothing more. It doesn't know what's being sent over it, and it offers no privacy at all: anything sent right now would travel as plain, readable bytes. That's the next problem. Once a connection exists, something has to travel over it securely and mean something specific, which is exactly where Part 2 picks up.