From packets to pixels: how the internet actually works
The path from a keystroke to a rendered page

From packets to pixels: how the internet actually works

Written on

I spent five years maintaining Swagger tools, I now maintain SpecLynx, UseArazzo and SwaggerExpert, and I spend my days building agents that talk to APIs. HTTP isn't a topic I visit occasionally; it's where I live. Which made it mildly uncomfortable to notice how much of my mental model of the internet I had never actually verified.

Most of what an experienced engineer knows about the layers beneath their own code is inherited rather than learned: absorbed from documentation skimmed at 2 a.m., from a colleague’s offhand explanation, from a bug that got fixed without ever being fully understood. That model works. It’s mostly right. But “mostly right” stays invisible until the day a DNS change stubbornly refuses to take effect, or a retried request duplicates an order — and you find out the gap had been sitting there the whole time.

So I went back and checked. Not the exotic corners: the basics. What a packet actually is. Why a bare domain traditionally can’t be a CNAME. What “DNS propagation” really means (nothing propagates anywhere). Where a browser spends its time between receiving bytes and showing you pixels. Most of it confirmed what I already believed. A few things corrected me.

This article is the write-up of that pass — the explanation I wish I’d been handed years ago, and the one I’d now give to anyone who builds on the web without having looked underneath it. It walks the whole path once, end to end: each section builds on the previous one, and each ends with hands-on exercises that need nothing more than a terminal and a browser.

Run them. That part isn’t decoration. Verifying something yourself is the difference between knowing it and having heard it — a distinction worth defending now that a confident, plausible explanation is always one prompt away. A language model will happily tell you how DNS works. dig +trace will show you.

A network of networks: packets, IPs, and ports

The internet is not a single thing you connect to. It is a network of networks: your home network, your ISP’s network, university networks, data-center networks — all agreeing to pass traffic between each other using the same set of rules (protocols). Nobody owns or runs the whole thing; the networks physically meet and swap traffic at Internet Exchange Points (IXPs) — buildings full of routers where dozens of providers plug into each other. Cooperation through shared protocols, not central control, is what holds it together.

One distinction worth fixing early: the internet is not the web. The internet is the plumbing — cables, routers, addresses, and the protocols that move data between machines. The web (websites, browsers, HTML, links) is just one application running on top of that plumbing, alongside email, video calls, online games, and messaging apps. This article covers the plumbing first, then climbs up to the web.

When you open a website, two machines are involved:

  • Client — the machine that asks for something (your laptop, your phone, a script).
  • Server — the machine that answers (a computer somewhere running software that listens for requests).

“Client” and “server” describe roles, not hardware. The same machine can be a client in one exchange and a server in another.

IP addresses: how machines find each other

Every device directly reachable on the internet has an IP address — a numeric label, like a postal address for computers.

  • IPv4: four numbers 0–255, e.g. 142.250.185.78. Only ~4.3 billion possible addresses — we’ve effectively run out.
  • IPv6: much longer hex addresses, e.g. 2a00:1450:4001:82f::200e. Designed to never run out.

Because IPv4 addresses are scarce, your home devices usually share one public IP address. Your router hands out private addresses (like 192.168.1.x) internally and translates between the two — a trick called NAT (Network Address Translation).

Packets: how data actually travels

Data is never sent as one continuous stream. It’s chopped into small chunks called packets (typically ~1,500 bytes). Each packet carries the destination IP, the source IP, a sequence number, and a slice of the actual data.

Packets travel independently. Two packets from the same message may take different routes through the network and arrive out of order — the receiving machine reassembles them. This design makes the internet resilient: if one route fails, packets simply flow around it. Between your machine and a server, packets hop through a chain of routers, each forwarding the packet closer to its destination.

Protocols: the shared rules

None of this works unless every machine speaks the same language. That’s what protocols are — agreed-upon formats and procedures, stacked in layers:

LayerProtocolWhat it does
Addressing & routingIPGets packets from one machine to another
TransportTCPReliable, ordered delivery; retransmits lost packets
TransportUDPFast, no guarantees; good for video calls, games
ApplicationHTTP, SMTP, DNS…Meaning of the data (web pages, email, name lookups)

TCP is worth understanding: before sending data, client and server perform a handshake (SYN → SYN-ACK → ACK) to establish a connection. TCP then numbers every byte, acknowledges receipt, and re-sends anything lost. The web runs mostly on TCP (though HTTP/3 now uses QUIC, built on UDP).

Ports: many conversations, one machine

A server has one IP address but runs many services. Ports (numbers 0–65535) distinguish them — like apartment numbers within one building:

PortService
80HTTP (web, unencrypted)
443HTTPS (web, encrypted)
22SSH (remote shell)
25 / 587SMTP (email)
53DNS

So a full “address” for a conversation is really IP + port: 142.250.185.78:443 means “the service listening on port 443 at that machine.”

You already use this daily as a developer without leaving your desk. localhost is a reserved name (resolving to 127.0.0.1) that means “this very machine” — client and server both on your own computer. When you run a dev server and open http://localhost:3000, your browser is the client, your project is the server, and 3000 picks it out among everything else running locally. Dev tools grab high, unclaimed ports by convention — 3000, 5173, 8000, 8080 — because the low, well-known ones require admin rights and are spoken for.

The whole trip, previewed

When you visit a website, roughly this happens — and the rest of this article unpacks each step:

  1. Your machine looks up the site’s IP address (DNS).
  2. It opens a TCP connection to that IP on port 443.
  3. Client and server negotiate encryption (TLS).
  4. Your browser sends an HTTP request.
  5. The server — wherever the site is hosted — answers, split into packets, routed hop by hop.
  6. Your browser reassembles and renders it.
🧪 Try it yourself
# What's my machine's local IP?
ip addr        # Linux
ipconfig       # Windows

# What IP does a domain resolve to? How long is the round trip?
ping example.com

# Watch your packets hop router by router
traceroute example.com     # macOS / Linux
tracert example.com        # Windows

Then find your public IP (search “what is my IP”) and compare it to your local one. They differ — that’s NAT at work.

✅ Check yourself
  • Why can two packets from the same download arrive out of order?
  • What problem does NAT solve, and what created that problem?
  • Your browser connects to 93.184.216.34:443. What do the two parts mean?
  • Why does a video call prefer UDP while a file download prefers TCP?

Speaking HTTP: requests, responses, and status codes

HTTP (HyperText Transfer Protocol) is the application-level protocol browsers and servers use to exchange web content. It’s a simple request–response conversation in (mostly) plain text: the client asks, the server answers, done.

HTTP is stateless — each request stands alone. The server doesn’t inherently remember you between requests. Anything that feels stateful (being logged in, a shopping cart) is built on top, usually with cookies or tokens sent along with every request.

Anatomy of a request

GET /articles/42 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
Cookie: session=abc123

Four parts: a method (GET), a path (/articles/42), headers (Name: value metadata lines), and an optional body — data you’re sending, e.g. form contents or JSON. GET requests carry no body; POST/PUT usually do.

MethodMeaningSafe?Idempotent?
GETRead a resourceYesYes
POSTCreate / submit dataNoNo
PUTReplace a resourceNoYes
PATCHPartially updateNoNot guaranteed
DELETERemove a resourceNoYes

Safe = doesn’t change anything on the server. Idempotent = doing it twice has the same effect as doing it once — which is why retrying a POST risks a duplicate order, while retrying a PUT doesn’t.

Keep in mind these meanings are conventions, not enforcement: the server runs whatever code it wants for any method. The conventions still matter, because everything around you — caches, browsers, proxies, retry logic — assumes you follow them.

Paths and query parameters

The path is how the server decides which resource or piece of code handles the request. On a static site, /about might map straight to an about.html file. In an API, paths typically name data and its hierarchy:

/users            → the collection of users
/users/42         → the user with id 42
/users/42/posts   → that user's posts

Query parameters — the ?key=value&key2=value2 tail of a URL — refine the request without changing which resource it targets:

/products?category=books&sort=price&page=2

Same resource (/products), but filtered, sorted, and paginated — the classic jobs of query parameters: filtering, searching, sorting, pagination. The server reads them as ordinary key–value inputs; nothing about them is magic.

Anatomy of a response

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5320
Cache-Control: max-age=3600
Set-Cookie: session=abc123; HttpOnly

<!DOCTYPE html>
<html>...

A status line, then headers, then the body — the actual content.

The first digit of the status code tells you the category. Memorize the shape, not the full list: 2 good, 3 look elsewhere, 4 your fault, 5 my fault.

RangeMeaningCommon examples
1xxInformational101 Switching Protocols
2xxSuccess200 OK, 201 Created, 204 No Content
3xxRedirection301 Moved Permanently, 302 Found, 304 Not Modified
4xxClient made a mistake400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
5xxServer failed500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable
📋 Headers worth knowing
HeaderDirectionPurpose
HostrequestWhich site on this server you want (one IP can host many sites)
Content-TypebothFormat of the body (text/html, application/json…)
AuthorizationrequestCredentials, e.g. Bearer <token>
Cookie / Set-Cookiereq / respClient-side state, sessions
Cache-ControlbothWhether/how long the response may be cached
LocationresponseWhere a redirect points
AcceptrequestFormats the client can handle

Not just pages: HTTP is the API protocol

Everything above applies far beyond loading web pages. When one program talks to another over the web — a mobile app syncing messages, JavaScript refreshing a feed, one backend calling another — it’s almost always the same HTTP machinery carrying JSON instead of HTML:

GET /api/me            →    200 OK
                            Content-Type: application/json

                            { "id": 7, "name": "Sam", "role": "admin" }

Nothing new is happening: same methods, same status codes, same headers — only the body format and the consumer change. The response isn’t shown to a human; code reads it and updates the page or acts on it. This is why learning HTTP once pays off twice: it’s both how websites load and how virtually every web API works.

HTTPS: HTTP + encryption

Plain HTTP is readable by anyone on the path — your Wi-Fi neighbors, your ISP, anyone in between. HTTPS wraps HTTP inside TLS (Transport Layer Security), which adds three guarantees:

  • Encryption — nobody in the middle can read the traffic.
  • Integrity — nobody can modify it undetected.
  • Authentication — a certificate, signed by a trusted Certificate Authority, proves you’re talking to the real example.com, not an impostor.

The padlock in your browser means the TLS handshake succeeded and the certificate checks out. It does not mean the site itself is trustworthy — phishing sites use HTTPS too. Certificates are free today (Let’s Encrypt), so there is essentially no excuse for a site to be HTTP-only.

HTTP versions in one paragraph

HTTP/1.1 (1997) — one request at a time per connection; browsers open several connections in parallel to compensate. HTTP/2 (2015) — multiplexes many requests over one connection, binary framing, header compression. HTTP/3 (2022) — runs on QUIC/UDP instead of TCP, avoiding a stalled packet blocking everything behind it. As a developer you rarely change your code between them — the semantics (methods, statuses, headers) stay the same.

🧪 Try it yourself
# See a raw response, headers included
curl -i https://example.com

# Headers only
curl -I https://example.com

# Follow redirects and watch them happen
curl -iL http://github.com

# Send JSON
curl -X POST https://httpbin.org/post \
  -H "Content-Type: application/json" \
  -d '{"hello": "world"}'

Then open DevTools → Network, reload any page, and click a request. Everything in this section is right there: method, status, headers, body.

✅ Check yourself
  • Why does a retry of a POST risk creating a duplicate order, while retrying a PUT doesn’t?
  • What’s the difference between 401 and 403?
  • What three guarantees does TLS add on top of HTTP?
  • The server sends Cache-Control: max-age=3600. What happens if you reload the page after ten minutes?

Domain names: leasing your corner of the namespace

Machines find each other by IP address, but 142.250.185.78 is hostile to humans — and a site’s IP can change when it moves servers. Domain names are stable, memorable labels (example.com) that get translated to IP addresses on demand (that translation is DNS). This section is about the names themselves: who owns them, who manages them, and how the pieces fit together.

Anatomy of a domain

Read a domain right to left — it’s a hierarchy:

 blog.example.com.
  │      │     │ └── root (implicit trailing dot)
  │      │     └──── top-level domain (TLD)
  │      └────────── second-level domain (the part you register)
  └───────────────── subdomain (you create these freely)
  • Root — the invisible top of the hierarchy, managed globally.
  • TLD.com, .org, .dev, plus country codes like .cz, .de, .uk. Each TLD is operated by a registry.
  • Second-level domainexample in example.com. This is the unit you actually register and pay for.
  • Subdomainsblog., shop., api. — once you own example.com, you can create unlimited subdomains at no cost, just by adding DNS records.

www is nothing special — just a conventional subdomain. www.example.com and example.com are technically two different names, which is why sites configure one to redirect to the other.

A URL contains more than the domain:

https://blog.example.com:443/posts/hello?ref=home#comments
└─┬──┘  └───────┬───────┘└┬┘└────┬─────┘└───┬───┘└───┬───┘
scheme       domain      port   path      query   fragment

The players

Three roles, often confused:

RoleWho they areExample
RegistryOperates an entire TLD, keeps its master databaseVerisign runs .com; CZ.NIC runs .cz
RegistrarRetail company that sells registrations to the publicNamecheap, Cloudflare, Porkbun
RegistrantYou — the person/company holding the domain

Above the registries sits ICANN, the nonprofit that coordinates the global naming system and accredits registrars.

Important mental model: you never buy a domain — you lease it. Registration is for 1–10 years and must be renewed. Miss the renewal and the domain eventually returns to the open market (after a grace/redemption period), where domain squatters are happy to grab anything with traffic.

What registration actually gets you

When you register example.com at a registrar:

  1. The registrar records you as registrant in the .com registry database.
  2. You provide contact details (WHOIS data — usually hidden behind free privacy protection nowadays).
  3. You tell the registry which nameservers answer questions about your domain — the hook that connects your domain to DNS. By default these are your registrar’s nameservers, but you can point them anywhere (e.g. Cloudflare’s).

That’s all a domain is: an entry in a registry database plus a delegation saying “ask these nameservers about this name.”

💡 Practical notes on choosing and managing domains
  • Pricing games: first-year prices are often loss-leaders ($0.99!) with steep renewal prices. Always check the renewal cost.
  • TLD choice: .com is still what people type on autopilot. Country TLDs are fine for local audiences. Some TLDs (.dev, .app) force HTTPS at the browser level — a nice property.
  • Transfers: you can move a domain between registrars. It requires unlocking the domain and an authorization code — a mild hassle by design, to prevent theft.
  • Domain ≠ hosting: registering a domain gives you a name, not a place to put a website — even if registrars love to bundle them.
  • Security: protect the registrar account with 2FA. Whoever controls the account controls the nameservers — and therefore your email, website, everything.
🧪 Try it yourself
  1. Look up any domain’s public record: whois example.com in a terminal (or a web WHOIS tool). Find the registrar, creation date, expiry date, and nameservers.
  2. Price a domain you like at two different registrars — compare first-year vs. renewal pricing.
  3. Find the nameservers of a big site: dig NS github.com +short.
✅ Check yourself
  • In docs.api.example.org, which part did someone pay for, and which parts are free?
  • What’s the difference between a registry and a registrar?
  • Why is “buying a domain” technically the wrong phrase?
  • You registered a domain but haven’t set up hosting. What does a visitor see, and why?

Hosting: where websites actually live

Hosting means keeping your site’s files and code on a computer that is switched on, connected to the internet, and reachable 24/7 — so that when someone’s browser asks for your site, something answers.

You could host a site from your laptop. In practice you don’t, because your laptop sleeps, your home IP changes, your upload bandwidth is small, and one viral moment would melt it. So you rent capacity from companies whose entire job is keeping servers online.

Static vs. dynamic — the distinction that matters

Static hosting: your site is just files — HTML, CSS, JavaScript, images — prepared in advance. The server’s only job is to hand them out, unchanged, to whoever asks. Every visitor gets the same files. It’s extremely cheap (often free), extremely fast, and there’s almost nothing to break or hack. Perfect for portfolios, blogs, documentation, and any app whose logic runs entirely in the browser. Typical providers: GitHub Pages, Netlify, Vercel, Cloudflare Pages.

Note: a static site can still feel dynamic — JavaScript in the browser can fetch data from APIs. “Static” describes the hosting, not the user experience.

Dynamic hosting: your site is a program that runs on the server and builds responses on the fly — reading databases, checking who’s logged in, processing payments. Different visitors get different responses. It needs a runtime (Node.js, Python, PHP, Go…) executing server-side. This is what anything with accounts, user-generated content, or a database requires.

Flavors of dynamic hosting

ModelWhat you manageExamples
Shared hostingYour files; the host runs everythingClassic cPanel hosts
VPS (virtual private server)The whole OS — you're the adminDigitalOcean, Hetzner, Linode
PaaS (platform as a service)Just your code; platform handles serversHeroku, Render, Railway, Fly.io
Serverless / functionsIndividual functions that run per-requestAWS Lambda, Cloudflare Workers
Cloud (IaaS)Everything, at any scaleAWS, Google Cloud, Azure

The trade-off runs one direction: more control ↔ more responsibility. A VPS gives you full freedom and full ownership of every security patch; a PaaS takes both away.

CDNs: hosting’s speed layer

A CDN (Content Delivery Network) is a worldwide fleet of servers that keep cached copies of your content close to users. A visitor in Tokyo gets your files from a Tokyo edge server instead of crossing the planet to your origin machine.

What CDNs buy you: speed (physics is real; shorter distance = lower latency), resilience (traffic spikes hit the edge cache, not your origin), and protection (most CDNs absorb DDoS attacks and terminate TLS for you).

Static assets are ideal CDN material. Modern static hosts (Netlify, Vercel, Cloudflare Pages) are effectively CDNs — your whole site lives at the edge. Common standalone CDNs: Cloudflare, Fastly, CloudFront.

Deployment: getting code onto hosting

Deployment is the process of moving your site from your machine to the host:

  1. The old way: drag files onto the server over FTP. Error-prone, no history, no undo.
  2. The current way — git-based: push to a Git repository; the platform detects the push, builds the site, and publishes it automatically (CI/CD).
git push → platform builds → tests run → deploy to edge → live in ~1 minute

Good platforms add preview deployments (every branch gets its own URL), instant rollback (bad release? one click back), and environment variables (secrets like API keys configured on the platform, never committed to the repo).

Custom domains: connecting the name to the host

Hosting providers give you a working default address like yourproject.netlify.app. To serve the site at your own domain:

  1. Register the domain (previous section).
  2. In the hosting dashboard, add the custom domain — the host now expects traffic for that name.
  3. In your DNS settings, point the domain at the host — an A record to an IP, or a CNAME to the host’s address (exact records in the next section).
  4. The host provisions a free TLS certificate automatically, so HTTPS just works.

The domain, the DNS, and the hosting can live at three different companies — they cooperate through exactly this mechanism.

🧪 Try it yourself
  1. Create a file index.html with anything in it, push it to a GitHub repository, and enable GitHub Pages in the repo settings. You now have a hosted website — total cost: zero.
  2. Open DevTools → Network on a big site and look at response headers like cf-cache-status, x-cache, server, or age — evidence of a CDN answering instead of the origin.
✅ Check yourself
  • Your blog is pure HTML/CSS. Which hosting model fits, and roughly what should it cost?
  • Why can’t a login system run on purely static hosting?
  • What problem does a CDN solve that faster servers cannot?
  • What are the moving parts between git push and your change being live?

DNS: the lookup chain behind every click

DNS (Domain Name System) is the internet’s phone book: a global, distributed database that translates names (example.com) into IP addresses (93.184.216.34) and other information. Every website visit, email delivery, and API call starts with a DNS lookup.

No single server holds all the answers. DNS is a hierarchy of delegation: the root knows who runs each TLD, the TLD knows who runs each domain, and the domain’s own nameservers hold the actual records.

A lookup, step by step

You type blog.example.com. Here’s the full journey (the “cold” path — caching usually shortcuts most of it):

  1. Browser & OS cache — have we looked this up recently? If yes, done.
  2. Resolver — your machine asks a recursive resolver (run by your ISP, or a public one like 1.1.1.1 or 8.8.8.8). The resolver does the legwork:
  3. Root nameservers → “Who handles .com?” → referral to the .com TLD servers.
  4. TLD nameservers → “Who handles example.com?” → referral to that domain’s authoritative nameservers (the ones set at the registrar).
  5. Authoritative nameservers → “What’s the address of blog.example.com?” → the actual answer: 93.184.216.34.
  6. The resolver returns the answer — and caches it for next time.

Total cost: a few milliseconds when cached, maybe 20–120 ms cold.

Two roles worth keeping straight: the recursive resolver is the asker that chases referrals on your behalf; an authoritative nameserver is the answerer that holds the real records for a domain.

DNS records

A domain’s zone is a set of typed records. The ones you’ll actually touch:

RecordMapsExample use
Aname → IPv4 addressexample.com → 93.184.216.34
AAAAname → IPv6 addresssame, for IPv6
CNAMEname → another namewww → example.com, or blog → myapp.netlify.app
MXdomain → mail servers (with priority)route email to Google Workspace / Fastmail
TXTname → arbitrary textownership proofs; email security (SPF, DKIM, DMARC)
NSdomain → its authoritative nameserversthe delegation itself

Rules of thumb:

  • A/AAAA when you have an IP; CNAME when you have a hostname. A CNAME says “same answers as that other name” — so it can’t coexist with other records on the same name, which is why the bare domain traditionally needs an A record, not a CNAME.
  • MX is why your website and your email can live at completely different companies under one domain.
  • TXT is the duct tape of DNS — every service that says “add this record to verify ownership” is using TXT.

Caching, TTL, and the myth of “propagation”

Every record carries a TTL (time to live) in seconds — how long any cache may keep the answer before asking again.

example.com.   3600   IN   A   93.184.216.34
                └── cache me for up to 1 hour

High TTL (hours/days): fewer lookups, faster for users — but changes take longer to be seen everywhere. Low TTL (60–300 s): changes spread fast, at the cost of more lookup traffic. Pro move: before a planned migration, lower the TTL to 300 a day in advance; make the change; raise it back.

When people say a DNS change is “propagating,” nothing is actually being pushed anywhere. Your authoritative server answers with the new value immediately. What you’re waiting for is the world’s caches to let their old copies expire (per the old TTL). Different resolvers cached at different moments, so some users see the new value while others still see the old one. This is normal and resolves itself within the old TTL.

Connecting a domain to hosting, in records

The custom-domain workflow from the hosting section, expressed concretely:

GoalRecord you create
Bare domain → your host's IPA example.com → 76.76.21.21
www → same siteCNAME www → example.com
Subdomain → a platformCNAME blog → myapp.netlify.app
Email at your domainMX example.com → mail provider
Prove ownership to a serviceTXT example.com → "verification=…"

And remember the top of the chain: the NS records set at your registrar decide whose DNS answers at all. Many people register at one company but point NS to Cloudflare and manage records there.

🧪 Try it yourself
# Basic lookup
dig example.com +short

# Full detail — find the TTL in the ANSWER section
dig example.com

# Specific record types
dig example.com MX +short
dig example.com TXT +short
dig example.com NS +short

# Watch the whole delegation chain (root → TLD → authoritative)
dig example.com +trace

# Ask a specific resolver
dig @1.1.1.1 example.com +short

Run dig on the same domain twice and compare the TTLs — the second answer comes from cache, counting down.

✅ Check yourself
  • What’s the difference between a recursive resolver and an authoritative nameserver?
  • Why can’t the bare example.com be a CNAME (in classic DNS)?
  • You changed an A record with TTL 86400 and some users still see the old site five hours later. Is anything broken?
  • Which record type controls where your email goes?

The browser: from response bytes to rendered pixels

A browser is a program that turns a URL into an interactive page. That involves four distinct jobs, in a pipeline:

fetch → parse & render → execute JavaScript → store data / stay interactive

Under the hood, each browser is built on an engine: Chrome and Edge use Blink (with the V8 JavaScript engine), Firefox uses Gecko (SpiderMonkey), Safari uses WebKit (JavaScriptCore). Engines differ slightly, which is why “works in Chrome” isn’t a guarantee.

Fetching

You press Enter. The browser resolves the domain via DNS, opens a TCP + TLS connection, sends an HTTP request, and receives an HTML response.

But one HTML file is never the whole page. As the browser reads the HTML, it discovers more things to fetch — stylesheets, scripts, images, fonts, API calls — and fires off dozens of additional requests, largely in parallel. A typical page today triggers 50–100 requests. The browser also decides what it can skip: resources cached from a previous visit (per Cache-Control) are reused straight from disk.

Rendering: the critical path

  1. Parse HTML → DOM. The HTML text becomes the DOM (Document Object Model) — a live tree of elements. Broken HTML doesn’t stop it; browsers repair as they go.
  2. Parse CSS → CSSOM. All stylesheets become a second structure describing the rules.
  3. DOM + CSSOM → render tree. Only visible elements, each with its final computed style.
  4. Layout (reflow). Compute exact geometry — where every box sits and how big it is.
  5. Paint. Fill in pixels: text, colors, borders, images.
  6. Composite. Layers (e.g. things with animations or position: fixed) are assembled on the GPU into the final frame.

Two practical consequences:

  • CSS blocks rendering — the browser won’t paint until it knows the styles (otherwise you’d see ugly unstyled flashes).
  • Classic <script> tags block parsing — the parser stops, runs the script, then continues. That’s why scripts go at the end of <body> or carry defer/async attributes.

When JavaScript later changes the page, the browser redoes the necessary parts of this pipeline — which is why heavy DOM manipulation can make pages feel sluggish.

Running JavaScript

JavaScript is what makes pages do things. The browser’s JS engine executes code that can read and modify the DOM, react to events (clicks, typing, scrolling), fetch more data without reloading (fetch() — the basis of every single-page app), and — with permission — draw, animate, play audio, or access camera and location.

One crucial constraint: JavaScript on a page runs on a single main thread, shared with rendering. Long-running JS = frozen page. And for safety, all of it runs in a sandbox: page code cannot touch your files or other tabs, and the same-origin policy stops a page from reading data belonging to another site.

Storing site data

Browsers give sites several places to keep data on your machine:

StorageSizeLifetimeTypical use
Cookies~4 KB eachset by expiry; sent to the server with every requestsessions, "keep me logged in"
localStorage~5–10 MBuntil clearedpreferences, e.g. dark mode
sessionStorage~5 MBuntil the tab closestemporary state
IndexedDBlargeuntil clearedoffline apps, big structured data
Cache Storagelargemanaged by service workersoffline pages, PWAs

The key difference: cookies travel to the server automatically; the rest stay in the browser unless code sends them. That’s why login sessions use cookies — and also why cookies are the mechanism behind cross-site tracking, and why browsers increasingly restrict third-party cookies.

DevTools: X-ray vision

Every desktop browser ships professional-grade inspection tools — press F12 or right-click → Inspect. The tabs map exactly onto this article:

TabWhat it shows
ElementsThe live DOM + applied CSS; edit both in place
ConsoleJS errors and logs; run any JavaScript against the page
NetworkEvery request: method, status, headers, timing, size
SourcesThe page's code; set breakpoints, step through JS
ApplicationCookies and all storage types, inspectable and editable
Performance / LighthouseWhere rendering time goes; automated audits
🧪 Try it yourself
  1. Open DevTools → Network, tick “Disable cache,” reload a news site. Count the requests; sort by size; find the slowest one.
  2. In Elements, double-click any headline on any website and rewrite it. (Only your local copy changes — refresh to restore.)
  3. In Console, run document.title = "I control this page" and watch the tab.
  4. In Application, inspect the cookies of a site you’re logged into — find the session cookie, note its HttpOnly and Secure flags.
  5. Run a Lighthouse audit on your favorite site — most of its suggestions will now make sense.
✅ Check yourself
  • Why does one page load involve dozens of HTTP requests?
  • In the rendering pipeline, what’s the difference between layout and paint?
  • Why does a while(true) {} loop freeze the whole page, including scrolling?
  • Your login persists across browser restarts. Which storage mechanism is involved, and why that one?

The whole story, in six lines

  1. Machines find each other by IP and exchange packets under shared protocols.
  2. On top of that, HTTP is the request/response language of the web; HTTPS encrypts it.
  3. Domain names give humans stable names, leased through registrars.
  4. Hosting is where the site’s files or code actually live and answer requests.
  5. DNS connects the name to the host — records, caches, and TTLs in between.
  6. The browser ties it all together: fetch, render, run, store, inspect.

Next time a page loads in 300 ms, you’ll know exactly how much machinery just cooperated to make that happen — and next time something breaks, you’ll know which layer to interrogate first.

Fork me on GitHub