It's the kind of thing you do hundreds of times a day without thinking about it. You open a browser, type something like https://github.com, press Enter, and a page appears. The whole thing takes less than a second. Behind that second is one of the most intricate sequences of operations in all of computing — a chain of protocols, servers, handshakes, and transformations that almost nobody fully thinks about until they're debugging something that broke somewhere in the middle of it.
As a software developer, understanding what happens in that gap between Enter and page load isn't trivia. It's the foundation. DNS, TCP, TLS, HTTP — these aren't abstract concepts that live in certifications. They're the machinery your code runs on top of every single day. When something goes wrong — a timeout, a 502, a CORS error, a certificate warning — the developer who understands this sequence can pinpoint the problem in minutes. The developer who doesn't spends hours guessing.
This article walks through the full sequence, step by step, in the order it actually happens.
Step 1: Parsing the URL
Before any network activity happens at all, the browser parses what you typed. A URL like https://github.com/explore is broken into distinct components:
Scheme: https — tells the browser which protocol to use
Host: github.com — the domain name of the server to contact
Path: /explore — the specific resource to request on that server
Port: not shown, but implied — https defaults to port 443
If you'd typed http:// instead of https://, the implied port would be 80 and the connection would be unencrypted. Most browsers today will automatically redirect you to the https version, but the distinction matters.
Step 2: Checking the Cache
Before doing anything on the network, the browser checks whether it already has a valid, cached response for this URL. If you visited github.com five minutes ago and the server told the browser to cache the response for ten minutes, the browser can skip the entire network sequence and show you the cached page immediately.
This is why web pages sometimes feel instant on revisit — you're not actually hitting the network at all. Caching headers like Cache-Control, Expires, and ETag are how servers communicate to browsers (and intermediate CDN servers) how long a resource should be considered fresh. Understanding caching is important for every web developer, because a misconfigured cache is a common source of bugs where users see stale content — old JavaScript, outdated styles, yesterday's data — long after you've deployed an update.
Step 3: DNS Resolution — Turning the Domain into an IP Address
Assuming there's nothing useful in the cache, the browser needs to contact github.com. But the internet doesn't route traffic by domain name — it routes by IP address. Something like 140.82.121.4. DNS (Domain Name System) is the system that translates between the two.
The resolution process goes like this:
Browser DNS cache: The browser first checks its own DNS cache. If it has recently looked up github.com, it uses the stored IP.
OS DNS cache: If the browser cache misses, the OS checks its own resolver cache.
/etc/hosts file: Before hitting a network DNS server, the OS checks a local file called /etc/hosts (on Linux and macOS) that can manually map domain names to IPs. This is how developers often configure localhost or test custom domains locally.
Recursive resolver: If all local caches miss, the OS sends a DNS query to a recursive resolver — typically one provided by your ISP or a public one like Google's 8.8.8.8 or Cloudflare's 1.1.1.1.
The DNS hierarchy: If the recursive resolver doesn't have the answer cached either, it works up the DNS hierarchy — querying a root nameserver, then a TLD nameserver (responsible for .com), then the authoritative nameserver for github.com itself, which returns the actual IP address.
The whole DNS resolution process, if it has to go all the way to the authoritative nameserver, typically takes somewhere between 20 and 120 milliseconds. Once it completes, the browser has an IP address and can actually make a network connection.
Step 4: Opening a TCP Connection
With an IP address in hand, the browser opens a TCP (Transmission Control Protocol) connection to the server. TCP is the transport layer protocol that most web traffic is built on — it provides a reliable, ordered channel over the otherwise unreliable packet-based internet.
Opening a TCP connection involves a three-way handshake:
The browser sends a SYN (synchronize) packet to the server.
The server responds with a SYN-ACK (synchronize-acknowledge).
The browser sends an ACK (acknowledge) back to the server.
At this point, the connection is established and data can flow. But if you're connecting over https, there's one more step before any HTTP traffic moves.
Step 5: The TLS Handshake
HTTPS is HTTP layered on top of TLS (Transport Layer Security). TLS is what encrypts the connection, ensuring that data can't be intercepted or tampered with in transit. This matters enormously — without it, anyone on the same network as you (a coffee shop Wi-Fi, a corporate network, an ISP) could read or modify the data passing between your browser and the server.
The TLS handshake is a sequence of back-and-forth messages that accomplishes three things:
Authentication: The server presents a TLS certificate — a digitally signed document that proves it is who it says it is. The browser checks this certificate against a list of trusted Certificate Authorities (CAs) — organizations trusted to vouch for the identity of domains. If the certificate is valid, trusted, and matches the domain, the browser proceeds.
Key exchange: The browser and server agree on a shared encryption key using a mathematical process that allows them to generate the same secret key without ever transmitting it directly — meaning an eavesdropper who observed the entire handshake still can't decrypt the subsequent traffic.
Cipher agreement: They agree on which encryption algorithm to use for the rest of the session.
Modern TLS (TLS 1.3) completes this in one round trip, reducing the performance overhead compared to older versions. When you see a padlock in your browser's address bar, you're looking at the end result of this handshake completing successfully.
Step 6: The HTTP Request
With a TCP connection open and (for HTTPS) TLS negotiated, the browser can finally send an HTTP request. For a page load, this is typically a GET request:
GET /explore HTTP/2
Host: github.com
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml,...
Accept-Language: en-US,en;q=0.9
Cookie: _gh_sess=...; user_session=...
The Host header tells the server which domain is being requested — this matters because a single server can host hundreds of different domains (a practice called virtual hosting), and the server needs to know which one to serve. The Cookie header sends any cookies stored for this domain, which is how the server recognizes who you are between requests. The User-Agent tells the server what kind of browser or client is making the request.
When you submit a form, create a resource, or send data, the browser typically uses a POST request instead, which includes a request body containing the data.
Step 7: The Server Processes the Request
On the other end of the connection, a server receives your request. For something like GitHub, that server isn't just a single machine — it's a fleet of servers behind a load balancer, which distributes incoming requests across many backend servers to handle the volume.
What happens next depends entirely on the application. A simple static site might just read an HTML file from disk and return it. A dynamic application like GitHub will:
Authenticate the session (checking cookies or tokens against a database)
Query one or more databases for relevant data
Apply business logic to determine what to show
Render a response — either a full HTML page, or increasingly often, JSON data that the frontend JavaScript will use to build the page
This is where your code lives, as a backend developer. HTTP requests come in; responses go out. Everything in between is the application.
Step 8: The HTTP Response
The server sends back an HTTP response:
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 89234
Cache-Control: no-store
Set-Cookie: _gh_sess=...; Path=/; Secure; HttpOnly
<!DOCTYPE html>
...
The status code (200 OK) tells the browser what happened. The most common status codes you'll encounter as a developer:
200 OK — success
301 / 302 — redirect (the resource has moved)
400 Bad Request — the client sent something malformed
401 Unauthorized — authentication required
403 Forbidden — authenticated but not permitted
404 Not Found — resource doesn't exist
500 Internal Server Error — something broke on the server
502 Bad Gateway — a proxy or load balancer couldn't reach the backend
The response headers also include caching instructions (Cache-Control), the content type, and often Set-Cookie headers to store or update cookies in the browser.
Step 9: The Browser Renders the Page
Once the HTML starts arriving, the browser begins rendering. This is its own complex pipeline:
HTML parsing builds a DOM (Document Object Model) — a tree representation of the page structure.
CSS parsing builds a CSSOM (CSS Object Model) — a tree of style rules.
JavaScript execution can both read and modify the DOM and CSSOM, often triggering additional network requests to APIs.
Layout calculates the size and position of every element.
Painting draws pixels to the screen.
As the HTML is parsed, the browser discovers references to additional resources — CSS files, JavaScript files, images, fonts — and fires off additional HTTP requests for each of them, going through the same DNS/TCP/TLS/HTTP process (though connections to the same server are reused via a mechanism called keep-alive).
A non-trivial web page might make 50–100 individual HTTP requests before it's fully loaded. The browser optimizes this aggressively — fetching resources in parallel, reusing connections, and using HTTP/2's multiplexing to send multiple requests over a single TCP connection simultaneously.
Where Things Go Wrong
Understanding this sequence makes debugging far more systematic. Most network-level problems announce which step they failed at if you know how to read the signals:
DNS resolution failure: ERR_NAME_NOT_RESOLVED — the domain name couldn't be translated to an IP. Usually a misconfigured DNS record or a typo in the URL.
TCP connection refused: ERR_CONNECTION_REFUSED — the server isn't listening on that port. Common when a development server isn't running.
TLS certificate errors: NET::ERR_CERT_AUTHORITY_INVALID or similar — the certificate is expired, self-signed without being trusted, or doesn't match the domain. Every developer eventually hits this locally when setting up HTTPS on a dev environment.
HTTP 4xx errors: The server understood the request but refused or couldn't fulfill it. Usually an application-level problem — authentication, missing data, bad input.
HTTP 5xx errors: Something broke on the server. Time to check your logs.
CORS errors: These aren't network failures — they're security policy enforcements. The browser received a response just fine but refused to share it with your JavaScript because the server didn't include the right headers allowing cross-origin access.
Your browser's developer tools (Network tab) expose all of this. Every single request, its status code, timing, headers, and response body are visible there. It's one of the most powerful debugging tools available to a web developer, and learning to read it fluently changes how fast you can isolate problems.
Why This Matters for Everything You'll Build
Whether you're building REST APIs in Go, frontend apps in JavaScript, or backend services that call other services, every layer of this sequence touches your code:
You configure DNS records when you deploy.
You manage TLS certificates to keep HTTPS working.
You set response headers to control caching behavior.
You return HTTP status codes that tell clients what happened.
You read request headers and cookies to authenticate users.
The URL-to-page sequence isn't background plumbing to ignore once it's working. It's the interface between your code and the rest of the internet. The developers who understand it confidently aren't just better at debugging — they make better design decisions, write more secure code, and build systems that behave correctly under the full range of real-world conditions.
Next time you press Enter after typing a URL, you'll know exactly what's happening in that second.
Top comments (0)