ModernCS
Session 1.190 minFree preview

How a Page Arrives

Requests, HTML, the DOM, and DevTools as your first debugger.

By the end of this session you will be able to:

  • Name every step between pressing Enter on a URL and seeing pixels, and say which step failed when a page does not load
  • Read a document request in the DevTools Network panel: status code, content type, size, and the requests it triggered
  • Explain the difference between the HTML a server sent and the DOM the browser is holding, and prove that difference in DevTools in under a minute

Pressing Enter is not one thing

You type https://developer.mozilla.org/en-US/docs/Web/HTML and a page appears. Between those two moments the browser does five separate jobs, and each one fails differently.

First it splits the URL apart. https is the scheme, developer.mozilla.org is the host, /en-US/docs/Web/HTML is the path. Second, it asks DNS to turn that host name into an IP address, because the network routes to numbers, not names. Third, it opens a TCP connection to that address and, because the scheme is https, negotiates TLS on top of it so the traffic is encrypted. Fourth, it sends an HTTP request: a method (GET), the path, and a stack of headers describing what it wants. Fifth, the server sends back a response: a status line, response headers, and a body.

That body, for a web page, is a plain text file. Nothing more. You can see the whole exchange without a browser at all:

curl -sS -D - -o /dev/null https://example.com

-D - dumps the response headers to your terminal and -o /dev/null throws the body away. The first line back is the status line, then the headers:

HTTP/2 200
date: Tue, 04 Aug 2026 11:26:50 GMT
content-type: text/html
server: cloudflare
last-modified: Sat, 01 Aug 2026 09:39:03 GMT

Two things there are worth burning into memory. 200 means the server found the thing and is sending it. content-type: text/html is how the browser knows to run the HTML parser over the body instead of showing it as text or downloading it. Note also that under HTTP/2 the header names arrive lowercased, which is why you see content-type in curl and Content-Type in older documentation. Same header.

The status codes you will meet in your first month: 200 fine, 301 and 302 moved, go ask this other URL instead, 404 no such path on this server, 403 there is such a path but not for you, 500 the server crashed while building your page. The split that matters is who is at fault. A 404 is usually your URL. A 500 is never your URL.

curl -sS -o /dev/null -w 'status=%{http_code}\ntype=%{content_type}\nbytes=%{size_download}\n' https://example.com

-w prints only the fields you name. That one reports 559 bytes: the entire page is smaller than this paragraph.

The HTML is a snapshot; the DOM is what is actually there

The body that arrives is text like this:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Order status</title>
  </head>
  <body>
    <h1>Order status</h1>
    <p id="state">Pending</p>
  </body>
</html>

The browser reads that text top to bottom and builds a tree of objects out of it: an html object with a head and a body under it, the body holding an h1 and a p, the p holding a text node. That tree is the DOM, the document object model. Chrome's own documentation puts it plainly: HTML represents the initial page content, and the DOM represents the current page content.

They start identical and then drift apart, for two reasons.

The obvious one is script. Any JavaScript on the page can add, remove or rewrite nodes. Change that Pending to Shipped from code and the DOM says Shipped while the file on the server still says Pending.

The less obvious one catches beginners much harder: the parser silently repairs invalid markup. A <p> element is not allowed to contain a <div>, so if you write this,

<p>Total: <div>42</div></p>

the parser closes your paragraph before the div, and the DOM you get has an empty p, then a div, then a stray </p> that becomes a second empty paragraph. Your text editor shows one structure. The browser is holding a different one. When a style or a script targets the shape you typed instead of the shape that exists, this is usually why.

So there is a rule you will use for the rest of your career: never debug a page by reading your source file. Read the DOM.

DevTools is where you look at both

Open DevTools with Command+Option+J on macOS or Control+Shift+J on Windows and Linux. Two panels matter today.

The Network panel records every request the page makes. Open it, tick Disable cache and Preserve log, then reload. The first row is the document itself: the HTML. Every row under it is something the HTML asked for, which is why one page load produces dozens of requests. The columns are Name, Status, Type, Initiator, Size, Time and a Waterfall showing when each request started and how long it took. The filter buttons above narrow it down: Doc for documents, CSS, JS, Img, Fetch/XHR for requests made by script.

Click any row and you get tabs: Headers, Preview, Response, Initiator, Timing. Response is the one people skip and should not. It shows the exact bytes the server sent, before the parser touched them.

The Elements panel shows the opposite: the live DOM tree, right now, after parsing and after every script that has run. Right-click anything on a page and choose Inspect to jump straight to its node.

Put those two together and you have a real debugging move. Something is missing from the page. Is it missing from the Response tab too? Then the server never sent it, and your problem is on the server or in your URL. Is it in the Response but not in Elements? Then it arrived and something removed it, or the parser rearranged it. Two clicks, and you have cut the search space in half.

Try it

Do this now, in Chrome, on any real site.

  1. Open DevTools, go to Network, tick Disable cache, and reload the page.
  2. Click the very first row. From the Headers tab, write down three values: the status code, content-type, and the transferred size.
  3. Open the Response tab. Search it with Command+F for a sentence you can see on the actual page.
  4. Now run curl against the same URL:
curl -sS -o /dev/null -w 'status=%{http_code}\ntype=%{content_type}\n' https://example.com

Swap in your URL. Compare against what DevTools told you.

  1. Switch to Elements. Find any heading, right-click it, choose Edit as HTML, and type something else. The page changes immediately.
  2. Reload. Your text is gone.

Success condition: curl and DevTools report the same status code and the same content type, and you can say in one sentence why step 6 undid your edit. If the answer is "because I changed the DOM and not the file the server sends", you have the idea.

Common mistakes

  • Reading your source file instead of the DOM. Your editor shows what you wrote. Elements shows what exists. When they disagree, the DOM wins, because that is what the browser is actually rendering.
  • Assuming a 200 on the document means the page is fine. The document can be perfect while a stylesheet or image under it returns 404. Sort the Network panel by Status and look for the red rows before you blame anything else.
  • Debugging with the cache on. You fix a file, reload, see the old version, and decide your fix did not work. Leave Disable cache ticked whenever DevTools is open.
  • Confusing "the page is blank" with "the request failed". A 200 with an empty body, a request that never left because DNS failed, and a request blocked by the browser look nothing alike in the Network panel. Read the status before you guess.
  • Reloading before opening DevTools. The Network panel only records while it is open, so the list comes up empty and the page looks innocent.

Where this goes next

You can now see what arrives and what the browser makes of it. Next session, Semantic HTML & Forms, you write that HTML yourself: landmarks, labels, and native controls, structured so a screen reader can actually work through your form.

That was one session of 4 in this phase.

Frontend Web runs to 5 phases. Buy the whole course, or just the phase you need.