# What the Web Actually Is

> The web isn't magic - it's two programs talking. This guide builds the mental model everything else in Web Fundamentals depends on: browsers and servers, URLs and DNS, and the three languages (HTML, CSS, JS) that make a page.


---

# What the Web Actually Is

You type a web address, hit enter, and a page appears. That feels instant and a little magical, and
it hides a real, mechanical process: one program asking another program for something, over a network,
in a format both sides agree on. Nothing about it is mysterious once you've seen it happen slowly.

This guide is the slow version. It's the first guide in Web Fundamentals because everything else -
HTML, CSS, the DOM, forms, accessibility - assumes you already know what a browser and a server
actually are, and where each of the three web languages fits. Skip this and later guides will keep
saying "the browser does X" without ever having introduced you to what a browser is doing that for.

## The phases

1. **[The Client-Server Model](01-the-client-server-model.md)** - what a browser and a server actually
   are as programs, the request/response cycle, and watching a real request happen in DevTools.
2. **[URLs, DNS, and HTTP, Together](02-urls-dns-and-http-together.md)** - taking a URL apart, how a
   hostname becomes an IP address, and what a raw HTTP request/response looks like.
3. **[HTML, CSS, and JavaScript: Three Jobs, Three Languages](03-html-css-and-javascript-three-jobs-three-languages.md)** -
   structure, presentation, and behavior, shown on one page so you know exactly what each language is
   for before you go learn it.

No prior web knowledge assumed. If you can open a browser, you're ready.


---

# The Client-Server Model

A browser is a program. A server is also a program. That's the whole secret behind "how the web
works" - two pieces of software, running on two different computers, having a conversation. Once you
stop picturing "the internet" as some ambient cloud and start picturing two specific programs talking,
the rest of this category gets a lot easier to follow.

## What a browser actually is

Chrome, Firefox, Safari, Edge - each is a program whose job is to ask for content and draw it on
screen. When you type `example.com` and hit enter, the browser doesn't "go to a website" the way you'd
walk into a store. It sends a message across the network asking for data, waits, and when data comes
back, it reads that data and paints pixels: text here, an image there, a button in the corner.

The browser is a **client**: the side of the conversation that speaks first. It never receives
anything it didn't ask for. If a page seems to update live - a new comment appears, a stock price
ticks - that's the browser quietly sending more requests in the background, not the server reaching
out on its own.

## What a server actually is

A server is a program that sits and waits for requests, then answers them. It could be running on a
beefy machine in a data center or on a laptop under someone's desk - what makes it "a server" is the
job it does, not the hardware. Popular server programs include nginx, Apache, and countless custom
ones written in Node.js, Python, Go, or Rust.

A server's whole existence is a loop: listen for a request, figure out what's being asked for, send
back a response. It doesn't know or care what the requester looks like - a browser, a phone app, a
command-line tool - it answers what's asked, the same way every time.

📝 **Terminology.** **Client** = the program that asks (your browser). **Server** = the program that
answers (the machine hosting the site). Neither word describes a physical object - both are roles a
program plays in a given conversation. A single machine can even be a server to some connections and
a client to others.

## The request/response cycle

Here's the full loop, start to finish, for loading a simple page:

```mermaid
sequenceDiagram
  participant Browser as Browser (client)
  participant Server as Server
  Browser->>Server: 1. Request: "GET /index.html, please"
  Note right of Server: 2. Server finds the file
  Server-->>Browser: 3. Response: "200 OK, here's the HTML"
  Note left of Browser: 4. Browser reads the HTML
  Browser->>Server: 5. Request: "GET /style.css"
  Server-->>Browser: 6. Response: "200 OK, here's the CSS"
  Note left of Browser: 7. Browser paints the page
```

One page load is rarely one request. The first response usually contains an HTML document, and that
document mentions other things the browser needs - a stylesheet, a script, some images. The browser
reads the HTML, spots those mentions, and fires off more requests automatically. A page that looks
like a single blank-to-loaded flash is often a dozen or more of these request/response pairs happening
in parallel.

💡 **Key point.** Every request follows the same shape: the client says what it wants, the server says
whether it has it and hands it over if so. Multiply that by however many resources a page needs, and
you have a full page load.

## Watching it happen: the Network tab

You don't have to take this on faith - your browser will show you every request it makes. Open
DevTools (right-click any page and choose "Inspect," or press F12) and click the **Network** tab.
Reload the page.

You'll see a list fill in, one row per request: a file name, a status, a type, a size, how long it
took. Click any row and you get the full detail: the exact request the browser sent (its headers, its
method) and the exact response the server sent back (its status code, its headers, its body).

Try this on a real page:

1. Open DevTools, go to Network, and check "Disable cache" so nothing is skipped.
2. Reload the page.
3. Find the first row - it's the HTML document itself, usually named after the page's path.
4. Click it, open the "Response" or "Preview" tab, and look at the raw HTML that came back.
5. Scroll the list. You'll see `.css` files, `.js` files, images, maybe a font - each one a separate
   request/response pair, each one the same pattern from earlier in this phase.

⚠️ **Gotcha.** The Network tab only shows requests made *after* it's open (or after you reload with it
open). If you open DevTools on a page that already finished loading, you'll see an empty list until
you refresh.

This is the single most useful habit for understanding - and later debugging - anything on the web.
When something looks wrong, the Network tab tells you what was actually asked for and what actually
came back, no guessing required.

Check your understanding of client, server, and the request/response loop:

```quiz
[
  {
    "q": "In the client-server model, which side speaks first?",
    "choices": ["The server", "The client", "Whichever one has new data"],
    "answer": 1,
    "explain": "The client (your browser) always sends the request first. The server only ever replies."
  },
  {
    "q": "A page that loads text, a stylesheet, and three images typically involves:",
    "choices": ["Exactly one request", "One request per resource, each its own request/response pair", "No requests once the page is cached"],
    "answer": 1,
    "explain": "The HTML comes back first, and the browser fires additional requests for everything it references."
  },
  {
    "q": "What does the browser's Network tab show?",
    "choices": ["Only failed requests", "The actual requests sent and responses received", "The page's JavaScript source only"],
    "answer": 1,
    "explain": "It's a live log of every request the browser makes and every response it gets back, including headers and status."
  }
]
```


---

# URLs, DNS, and HTTP, Together

Phase 1 showed the request/response loop as a black box: browser asks, server answers. This phase
opens the box. A URL is where the request is headed, DNS is how that address gets found on the
network, and HTTP is the actual language the request and response are written in.

## Anatomy of a URL

Take this address apart:

```text
   https://shop.example.com/products/shoes?color=blue#reviews
   └─┬─┘   └──────┬───────┘└─────┬──────┘└────┬─────┘└──┬───┘
   scheme        host           path         query    fragment
```

- **Scheme** (`https`) - which protocol to speak. `https` is HTTP over an encrypted connection; plain
  `http` is the same protocol without encryption.
- **Host** (`shop.example.com`) - which server to talk to, by name. This is the piece DNS turns into an
  actual network address.
- **Path** (`/products/shoes`) - which resource on that server. Think of the host as a building and the
  path as the room number.
- **Query** (`?color=blue`) - optional `name=value` pairs after a `?`, joined by `&` if there's more
  than one. Extra instructions that don't change which path is being requested.
- **Fragment** (`#reviews`) - a pointer to a spot *within* the page, handled entirely by the browser
  after the page loads. It never gets sent to the server at all.

📝 **Terminology.** People say "link," "web address," and "URL" interchangeably. They're the same
thing - Uniform Resource Locator is the formal name.

⚠️ **Gotcha.** Because the fragment never reaches the server, a server has no way to know which
`#section` a visitor is jumping to. That's purely a browser-side behavior - relevant later when you
build single-page apps that use the fragment for routing.

## From hostname to IP address: DNS

Servers on a network are found by numeric address, an **IP address** like `93.184.216.34` - not by
name. `shop.example.com` is a name a human can remember; something still has to convert it to that
number before any request can be sent. That translation service is **DNS** (Domain Name System),
and it runs before HTTP ever starts.

The short version: your computer asks a DNS resolver "what's the IP for shop.example.com?", the
resolver looks it up (checking caches, then asking authoritative servers if needed), and hands back an
IP address. Only then does the browser open a connection to that address and send the actual HTTP
request.

💡 **Key point.** DNS runs *before* the request/response cycle from Phase 1, not as part of it. No IP
address, no connection, no request.

That's the whole picture at this altitude - DNS's caching layers, record types, and failure modes get
the full treatment in [How the Internet Works](/guides/how-the-internet-works).

## What HTTP actually looks like

HTTP is the format both sides speak once the connection is open. A request, written out in full:

```http
GET /products/shoes?color=blue HTTP/1.1
Host: shop.example.com
Accept: text/html
```

`GET` is the method - what kind of action this is. `/products/shoes?color=blue` is the path and query
from the URL. `Host` tells the server which site, since one server can answer for many hostnames. The
response looks like this:

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 3241

<!DOCTYPE html>
<html>...</html>
```

The first line is the **status code** - a three-digit verdict. You don't need to memorize the full
list, but recognizing the ranges gets you far:

| Range | Meaning | Example |
|-------|---------|---------|
| 2xx | Success | `200 OK` |
| 3xx | Redirected somewhere else | `301 Moved Permanently` |
| 4xx | The client's request was bad | `404 Not Found` |
| 5xx | The server broke | `500 Internal Server Error` |

📝 **Terminology.** **Method** = the verb of a request (`GET`, `POST`, and others). **Status code** =
the three-digit verdict at the top of a response. **Headers** = the labeled extra lines in both
messages (`Host`, `Content-Type`, and so on).

This is enough to read a request or response on sight. The full set of methods, the complete status
code table, headers, cookies, and HTTPS get their own dedicated guide:
[HTTP, Explained](/guides/http-explained).

See a full request travel from URL to response:

```playground-http
```

Lock in the pieces before moving to what actually renders once the response arrives:

```quiz
[
  {
    "q": "In https://shop.example.com/products/shoes?color=blue#reviews, which part is the path?",
    "choices": ["shop.example.com", "/products/shoes", "?color=blue", "#reviews"],
    "answer": 1,
    "explain": "The path identifies which resource on the server - the host is the server's name, the query and fragment come after it."
  },
  {
    "q": "What does DNS do?",
    "choices": ["Encrypts the connection", "Turns a hostname into an IP address", "Renders the HTML"],
    "answer": 1,
    "explain": "DNS resolves a name like shop.example.com to a numeric IP address before any request can be sent."
  },
  {
    "q": "A response starting with 404 means:",
    "choices": ["The server crashed", "The request succeeded", "The requested resource wasn't found"],
    "answer": 2,
    "explain": "4xx codes mean something about the client's request was the problem - 404 specifically means not found."
  }
]
```


---

# HTML, CSS, and JavaScript: Three Jobs, Three Languages

The response body from Phase 2 was HTML - but HTML alone gives you a plain, unstyled, static document.
Real pages layer on two more languages, each with one job. Mixing them up is the single most common
source of confusion for anyone starting out, so here's the split, made concrete on one small page.

## Three jobs

- **HTML (structure)** - what exists on the page and what it means: this is a heading, this is a
  button, this is a list. HTML answers "what is this thing," never "what does it look like" or "what
  does it do when clicked."
- **CSS (presentation)** - how the structure looks: colors, spacing, fonts, layout. CSS never adds
  content and never decides what happens on a click - only appearance.
- **JavaScript (behavior)** - what happens over time: responding to clicks, changing content after the
  page loaded, talking to a server for new data. JavaScript is the only one of the three that can react
  to anything.

📝 **Terminology.** This split has a name: **separation of concerns**. Each language owns one
responsibility, and a well-built page keeps them from bleeding into each other - structure in HTML,
look in CSS, interactivity in JS.

## One page, annotated

Here's a small newsletter signup box, showing all three doing their own job:

```html
<div class="signup">
  <h2>Get the newsletter</h2>
  <input type="email" id="email" placeholder="you@example.com">
  <button id="subscribe-btn">Subscribe</button>
  <p id="status"></p>
</div>
```
This is pure structure. A heading, a text input, a button, an empty paragraph reserved for a message
later. No color, no font, no click handling - only "here's what exists and what each piece is."

```css
.signup {
  padding: 24px;
  background: #f4f4f8;
  border-radius: 8px;
}
.signup button {
  background: #3457d5;
  color: white;
  border: none;
  padding: 8px 16px;
}
```
This is pure presentation. It takes the exact same structure and gives it a light gray card background,
rounded corners, and a blue button with white text. Delete this CSS entirely and the signup box still
works - it renders as plain black text on white with a default gray button.

```js
document.getElementById("subscribe-btn").addEventListener("click", function () {
  var email = document.getElementById("email").value;
  document.getElementById("status").textContent = "Subscribed: " + email;
});
```
This is pure behavior. Nothing happens until the button is clicked - HTML and CSS are both static and
sit there unchanged until this code runs. When the click happens, JavaScript reads the input's value
and updates the status paragraph. This snippet is illustrative, not meant to run standalone here - the
next guides show it running for real.

💡 **Key point.** Delete the CSS and the page still functions, only uglier. Delete the JavaScript and the
page still displays, it stops responding to clicks. Delete the HTML and there's nothing left for
the other two to act on. HTML is the foundation the other two attach to.

⚠️ **Gotcha.** It's possible to write CSS that fakes interactivity (a `:hover` color change) or HTML
that carries inline styling (`style="color: red"`) or inline scripting (`onclick="..."`). All three
work, and all three blur the separation this phase drew. As you get further into CSS and
JavaScript, keeping structure, presentation, and behavior in their own files - not tangled inline - is
what keeps a real page maintainable.

## Where to go next

You now know what each language is responsible for. The next three guides each go deep on one:

- **[HTML From Zero](/guides/html-from-zero)** - every structural element, written properly.
- **[CSS Without Tears](/guides/css-without-tears)** - selectors, the box model, and how presentation
  rules actually cascade.
- **[The DOM Explained](/guides/the-dom-explained)** - how JavaScript reaches into the page HTML
  built and changes it live, which is what that `addEventListener` call above was actually doing.

Start with HTML - everything else in this category attaches to it.

Check the three-way split sticks before moving on:

```quiz
[
  {
    "q": "Which language decides what happens when a button is clicked?",
    "choices": ["HTML", "CSS", "JavaScript"],
    "answer": 2,
    "explain": "Only JavaScript can react to events like clicks. HTML defines the button exists; CSS defines how it looks."
  },
  {
    "q": "If you delete all the CSS from a working page, what happens?",
    "choices": ["The page disappears entirely", "The page still shows content, only unstyled", "The JavaScript stops running"],
    "answer": 1,
    "explain": "CSS only controls appearance. The structure (HTML) and behavior (JS) are unaffected by removing it."
  },
  {
    "q": "What is 'separation of concerns' referring to in this phase?",
    "choices": ["Splitting a site across multiple servers", "Keeping structure, presentation, and behavior in their own languages", "Using multiple browsers to test a page"],
    "answer": 1,
    "explain": "It's the practice of giving HTML, CSS, and JS each their own single responsibility instead of mixing them."
  }
]
```
