# How the web works

> Trace what happens between typing a URL and seeing pixels: DNS, connections, HTTP, and the browser's rendering pipeline.

Frontend Developer roadmap · The web platform · 15 min read · https://levelcode.org/roadmaps/frontend/how-the-web-works

Every frontend bug you will ever chase happens somewhere along one journey: a URL becomes an IP address, a connection opens, an HTTP request goes out, a response comes back, and the browser turns bytes into pixels. If you can picture that journey, you can reason about slow pages, broken caches, missing styles and CORS errors instead of guessing. This lesson walks through each step in order.

## Clients, servers and URLs

The web is a conversation between two kinds of programs. A **client** (usually a browser) asks for things, and a **server** answers. Neither remembers the other between conversations unless something like a cookie is used to carry that memory.

The client says *what* it wants with a URL. Every part of a URL has a job:

```text
https://shop.example.com:443/products/shoes?color=red&size=42#reviews
└─┬─┘   └──────┬───────┘ └┬┘ └─────┬──────┘ └───────┬───────┘ └──┬──┘
scheme       host       port     path            query        fragment
```

| Part | Meaning |
| --- | --- |
| Scheme | The protocol: `https` (encrypted) or `http`. |
| Host | The server's name, which must be turned into an IP address. |
| Port | Which "door" on the server. Defaults to 443 for `https` and 80 for `http`, so it is usually omitted. |
| Path | Which resource on that server. |
| Query | Extra key/value parameters, separated by `&`. |
| Fragment | A position *inside* the page. It is never sent to the server; the browser handles it locally. |

## DNS: turning names into addresses

Computers on the internet find each other by **IP address**, such as `203.0.113.10` (IPv4) or `2001:db8:85a3::8a2e:370:7334` (IPv6). Humans prefer names. The **Domain Name System (DNS)** is the distributed phone book that maps one to the other.

When you visit `shop.example.com`, the lookup checks progressively further away until it finds an answer:

1. **Browser cache** – did this tab or another one resolve the name recently?
2. **Operating system cache** – including the local `hosts` file.
3. **Recursive resolver** – a server run by your ISP, your company, or a public provider. If it does not have the answer cached, it does the legwork:
   - asks a **root server**, which says "ask the `.com` servers";
   - asks the **`.com` TLD server**, which says "ask `example.com`'s authoritative servers";
   - asks the **authoritative server**, which returns the actual record.

The answer comes with a **TTL** (time to live) in seconds, telling every cache along the way how long it may reuse the answer. The record types you will meet most often are:

| Record | Points a name at | Example |
| --- | --- | --- |
| `A` | An IPv4 address | `shop.example.com → 203.0.113.10` |
| `AAAA` | An IPv6 address | `shop.example.com → 2001:db8::10` |
| `CNAME` | Another name (an alias) | `www.example.com → shop.example.com` |

You can query DNS yourself with `dig` (macOS and Linux) or `nslookup` (all platforms):

```bash
dig +short example.com A
```

```text
203.0.113.10
203.0.113.11
```

You will see real addresses rather than these placeholders, and large sites often return different ones depending on where you are.

> [!NOTE]
> TTLs are why "I updated my DNS but the site still points at the old server" is normal. Resolvers keep serving the cached answer until the TTL expires, which can take minutes or hours.

## Opening a connection: TCP, TLS and QUIC

With an IP address in hand, the browser opens a connection to the server. Each step costs **round trips** – a message there and a reply back – and on a mobile network one round trip can easily take 100 ms.

- **TCP** gives a reliable, ordered stream of bytes. Setting it up takes a three-way handshake (SYN, SYN-ACK, ACK): one round trip.
- **TLS** runs on top of TCP for `https`. It proves the server's identity using a **certificate** signed by an authority the browser trusts, then agrees on encryption keys. TLS 1.3 needs one more round trip.
- **HTTP/3** replaces TCP with **QUIC**, which runs over UDP and combines the transport and encryption handshakes into a single round trip.

That is why browsers work hard to **reuse** connections. Once a connection to a host is open, every later request to that host skips the handshakes entirely.

Here is the whole journey for a first visit to an `https` site, end to end. Press **Play** to walk through it one message at a time.

```sequence
title: Your first request to https://shop.example.com
participants: Browser, DNS resolver, Server
Browser -> DNS resolver: Where is shop.example.com?
> The browser checks its own cache first; on a miss it asks a resolver (usually your ISP's or a public one like 1.1.1.1).
DNS resolver --> Browser: 203.0.113.42
> The answer comes with a TTL, so the browser can reuse it without asking again for a while.
Browser -> Server: TCP SYN
> TCP's three-way handshake starts: one full round trip before any data moves.
Server --> Browser: SYN-ACK
Browser -> Server: ACK + TLS ClientHello
> The final ACK can carry the start of the TLS handshake.
Server --> Browser: ServerHello + certificate
> The browser checks the certificate is valid for this host and signed by an authority it trusts.
Browser -> Server: GET /products/shoes
> Only now — after DNS, TCP and TLS — does the actual HTTP request go out, encrypted.
Server --> Browser: 200 OK + HTML
Browser -> Browser: Parse HTML, request CSS/JS/images
> Most sub-resources come from the same host, so they reuse this connection and skip every handshake above.
```

## HTTP: the request and the response

HTTP is the language spoken over the connection. A request has a **method**, a **path**, **headers**, and sometimes a **body**. Written out in the readable HTTP/1.1 form, it looks like this:

```text
GET /products/shoes?color=red&size=42 HTTP/1.1
Host: shop.example.com
Accept: text/html
Accept-Language: en-GB
Cookie: session=abc123
```

The server replies with a **status code**, headers, and a body:

```text
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5120
Cache-Control: max-age=300

<!doctype html>
<html lang="en">…
```

HTTP/2 and HTTP/3 send the same information in a compact binary format and can carry many requests at once over one connection, but the *meaning* – methods, headers, status codes – is identical. Learn it once and it applies everywhere.

### Methods

| Method | Typical use |
| --- | --- |
| `GET` | Read a resource. Should not change anything on the server. |
| `POST` | Create something or trigger an action (submit a form). |
| `PUT` / `PATCH` | Replace / partially update a resource. |
| `DELETE` | Remove a resource. |

### Status codes

The first digit tells you the category:

| Range | Meaning | Common examples |
| --- | --- | --- |
| 2xx | Success | `200 OK`, `201 Created`, `204 No Content` |
| 3xx | Look elsewhere | `301 Moved Permanently`, `304 Not Modified` |
| 4xx | The client made a mistake | `400 Bad Request`, `401 Unauthorized`, `404 Not Found` |
| 5xx | The server failed | `500 Internal Server Error`, `503 Service Unavailable` |

### Headers worth knowing

- `Content-Type` – what the body is (`text/html`, `application/json`, `image/png`).
- `Cache-Control` – how long the response may be reused without asking again.
- `Location` – where to go next, sent with a 3xx redirect.
- `Set-Cookie` / `Cookie` – how a server asks the browser to remember a value and send it back on later requests. This is how a stateless protocol supports logins.

You can see real headers with `curl`. The `-I` flag sends a `HEAD` request, which returns only the headers:

```bash
curl -I https://example.com
```

```text
HTTP/2 200
content-type: text/html
cache-control: max-age=3600
content-length: 1256
```

Your exact headers will differ, but the shape is always the same.

## From bytes to pixels: the rendering pipeline

The HTML document is only the beginning. The browser reads it **as it streams in** and discovers everything else it needs.

```html
<!doctype html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="/styles.css">
    <script src="/app.js" defer></script>
  </head>
  <body>
    <h1>Shoes</h1>
    <img src="/img/red-shoe.jpg" alt="Red running shoe">
  </body>
</html>
```

Loading this page triggers at least four HTTP requests: the document, `styles.css`, `app.js`, and the image. Then the browser runs its pipeline:

1. **Parse HTML → DOM.** The parser turns tags into a tree of objects called the Document Object Model.
2. **Parse CSS → CSSOM.** Stylesheets become a tree of rules. CSS is **render-blocking**: the browser will not paint the page until the stylesheets in the `<head>` have arrived, to avoid flashing unstyled content.
3. **Run JavaScript.** A plain `<script>` is **parser-blocking**: the parser stops, downloads and runs the script, then continues. With `defer`, the script downloads in parallel and runs after the document has been parsed, in order. With `async`, it runs as soon as it downloads, in no guaranteed order.
4. **Style.** The browser works out the final styles for every visible element (elements with `display: none` are skipped).
5. **Layout.** It calculates the size and position of every box.
6. **Paint.** It fills in pixels: text, colours, borders, images.
7. **Composite.** It combines painted layers into the final image on screen.

When JavaScript later changes the page, the browser repeats only the steps it has to. Changing a colour needs a repaint; changing a width needs layout again, which is more expensive.

> [!TIP]
> Put `defer` on your scripts by default. It lets the HTML parser keep going while the script downloads, and guarantees the DOM exists when your code runs.

## Common misconceptions

- **"A web page is one file."** A typical page makes dozens of requests. Each image, font, stylesheet and script is fetched separately, usually over a reused connection.
- **"The server sees everything in the URL."** It never receives the `#fragment`. If you need the server to know something, put it in the path or query.
- **"A 404 means nothing came back."** Error responses usually have a body – the "page not found" page you see is that body.
- **"HTTPS just hides passwords."** It also prevents anyone on the network from reading or altering *any* content, and proves you reached the real server. Browsers block `http://` scripts on `https://` pages for exactly this reason (this is called mixed content).
- **"DNS changes are instant."** Cached answers live until their TTL expires.

## Practice

Trace a real page load end to end.

1. Run `dig +short` (or `nslookup`) for a site you use daily and note the addresses.
2. Run `curl -I` against the same site with and without `www.`. Note the status codes and any `Location` header.
3. Open the site in a browser, open DevTools (F12), go to the **Network** tab, tick **Disable cache**, and reload. Find the first request (the document), then answer: how many requests did the page make in total? What `Content-Type` did the document have? Click the document and open its **Timing** section – which phases appear before "Waiting for server response"?
4. Reload again *without* "Disable cache" and compare the number of bytes transferred.

**Solution**

What you should see (exact numbers vary by site):

1. One or more IP addresses. Big sites often return several, and they may differ from a friend's results in another city.
2. One of the two hostnames usually answers with a `301` or `308` and a `Location` header pointing at the other – the site has picked a canonical hostname and redirects to it.
3. The document has `Content-Type: text/html; charset=utf-8` (or similar). Most sites make 20–150 requests. The Timing panel shows **DNS Lookup**, **Initial connection** and **SSL** (the TLS handshake) before the request is sent – unless the connection was reused, in which case those phases are missing or near zero.
4. On the second load, many rows say "(memory cache)" or "(disk cache)", or return `304 Not Modified`, and far fewer bytes are transferred. That is `Cache-Control` and validation at work.

## Key takeaways

- A URL names the scheme, host, port, path and query; the fragment never leaves the browser.
- DNS resolves a hostname to an IP address through a chain of caches and servers, and TTLs control how long answers are reused.
- HTTPS connections cost round trips (TCP + TLS, or QUIC), so browsers reuse them.
- HTTP is requests (method, path, headers, body) and responses (status, headers, body); the meaning is the same across HTTP/1.1, 2 and 3.
- Browsers parse HTML into the DOM and CSS into the CSSOM, then style, lay out, paint and composite. CSS blocks rendering; plain scripts block parsing, so use `defer`.

## Quiz

1. A user visits https://example.com/docs?page=2#install. Which part of the URL does the server never receive?

   A. The path /docs
   B. The fragment #install
   C. The query ?page=2
   D. The host example.com

   **Answer: B.** The fragment is handled entirely by the browser to scroll to a position in the page. The host is sent in the Host header, and the path and query are part of the request line.

2. You change your domain's A record, but some users still reach the old server an hour later. What is the most likely reason?

   A. The browser is using HTTP/1.1 instead of HTTP/2
   B. The server is returning 304 Not Modified
   C. The TLS certificate has not been renewed
   D. Resolvers are still serving the cached record until its TTL expires

   **Answer: D.** DNS answers are cached for their TTL by browsers, operating systems and recursive resolvers. HTTP versions, 304 responses and certificates have nothing to do with which IP address a name resolves to.

3. Which status code means the client asked for something that does not exist?

   A. 404
   B. 500
   C. 301
   D. 204

   **Answer: A.** 4xx codes signal client errors, and 404 specifically means not found. 500 is a server failure, 301 is a permanent redirect, and 204 is a success with no body.

4. What does adding defer to a <script src="app.js"> tag do?

   A. Downloads the script only after the page has fully painted
   B. Runs the script as soon as it downloads, in any order
   C. Downloads in parallel with parsing and runs after the document is parsed, in order
   D. Prevents the script from blocking CSS from loading

   **Answer: C.** defer lets the parser continue while the script downloads, then runs deferred scripts in document order once parsing finishes. Running as soon as it downloads in any order describes async.
