The Browser Contract

What Happens When You Type a URL (The Full Version)

The full trip from pressing Enter to seeing pixels: DNS, TCP, TLS, HTTP, then reading the page and painting it. Caches skip most steps. Every other lesson in this series fits on this map.

Lesson 0018 min read#browser#networking#rendering#interview

On this page

  1. The map (memorize this first)
  2. Step 1: Input handling
  3. Step 2: Before the network (the shortcuts)
  4. Step 3: DNS (name to IP address)
  5. Step 4: Connection (TCP or QUIC)
  6. Step 5: TLS (the padlock)
  7. Step 6: The HTTP request
  8. Step 7: Server work (the TTFB story)
  9. Step 8: The response
  10. Step 9: Parsing (HTML to DOM, CSS to CSSOM)
  11. Step 10: Rendering (the critical rendering path)
  12. Step 11: After the load (where React apps spend their time)
  13. Worked example: a cold visit to a Next.js e-commerce site from Ahmedabad
  14. Where caching skips steps (summary)
  15. Common mistakes in interviews
  16. How to answer in an interview (the 2-minute version)
  17. Interview questions
  18. Key takeaways
  19. What's next

In one line: The browser finds out where the page lives, opens a secure connection to it, asks for the page, and turns the answer into pixels. A cache is a saved copy of something, kept so it does not have to be fetched or worked out again. Caches can skip almost every step on the way.

A junior answer to this question is "DNS lookup, then the server sends HTML." A senior answer goes through each layer. It says where time is spent, where caching skips work, and which tool shows it. This lesson builds that answer step by step. Every other lesson in this series zooms into one part of this map.

The map (memorize this first)

TEXT
You press Enter  │  ├─ 1. Input handling      → Is it a URL or a search? Normalize it.  ├─ 2. Before the network  → HSTS, Service Worker, HTTP cache, bfcache  ├─ 3. DNS                 → domain name  → IP address  ├─ 4. Connection          → TCP handshake (or QUIC for HTTP/3)  ├─ 5. TLS                 → encryption and certificate check  ├─ 6. HTTP request        → GET / with headers and cookies  ├─ 7. Server work         → CDN → load balancer → app → DB → response  ├─ 8. Response            → status, headers, compressed HTML  ├─ 9. Parsing             → HTML → DOM, CSS → CSSOM, JS execution  ├─ 10. Rendering          → style → layout → paint → composite  └─ 11. After load         → hydration, lazy resources, interactivity

Some words in the map are new, so here are short meanings. You will meet each one again below.

  • DNS (Domain Name System) is a service that turns a site name into an IP address.
  • TCP is a set of rules for sending data in order and without loss between two computers. QUIC is a newer set of rules that does the same job on top of UDP.
  • UDP is a simpler way to send data. It does not set up a connection first and does not resend lost data by itself.
  • TLS (Transport Layer Security) is the system that encrypts (scrambles) data so that only the two ends can read it. It is what makes https secure.
  • HTTP (HyperText Transfer Protocol) is the set of rules for how a browser asks for a page and how a server answers.
  • A header is a named line of extra information that is sent with a request or a response, for example Cache-Control: no-cache.
  • A cookie is a small piece of text that the server asks the browser to keep and send back on later requests.
  • A CDN (Content Delivery Network) is a group of servers around the world that keep copies of your files close to users.
  • HTML is the text that describes the content of a page. CSS is the text that describes how it looks. The DOM is the page as a tree of objects that JavaScript can change.
  • A render step turns code and data into the pixels you see on the screen.

Keep this map in your head during the interview. Walk it from top to bottom. Then you can zoom into any layer the interviewer cares about.

Step 1: Input handling

When you type in the address bar, the browser first decides what you meant:

  • react.dev looks like a domain, so the browser treats it as a URL.
  • how to use useState looks like a search, so the browser sends it to your default search engine.
  • localhost:3000 is a URL with a port.

Next, the browser normalizes the URL (cleans it into one standard form). It adds a scheme (like https://) if you left it out. It makes the host name lowercase. It changes international domain names into Punycode, which is a plain-letters form. For example, मेरा.com becomes a name that starts with xn--.

Why seniors mention it: the browser starts work early by predicting what you will do. Chrome may already look up DNS or open a connection while you type, because it expects you to press Enter. This early work is called prefetching (fetching something before you ask for it). This is why a site you visit often feels faster than its raw numbers suggest.

Step 2: Before the network (the shortcuts)

This is where senior answers stand out. Several layers can answer the request without using the network at all.

2a. HSTS: forcing HTTPS

HSTS (HTTP Strict Transport Security) is a rule that a site gives to the browser. It says: "always use HTTPS for this site, never plain HTTP." HTTPS is HTTP sent over TLS, so it is encrypted.

The browser knows this if the site sent a Strict-Transport-Security header before, or if the site is on the browser's HSTS preload list (a list of sites that is built into the browser). Then the browser changes http:// to https:// by itself. No insecure request is sent. Chrome shows this as a 307 Internal Redirect in DevTools.

Interview angle: without HSTS, the very first http:// request can be caught by someone on public Wi-Fi. This can happen before the server's redirect to HTTPS arrives. HSTS closes that gap.

2b. Service Worker

A Service Worker is a JavaScript file that a site installs in your browser and that runs in the background, apart from the page. It sits between the page and the network. It can catch a request and answer from its own saved copies. This is how PWAs (Progressive Web Apps, web apps that act like phone apps) work offline. Its answer comes before the normal HTTP cache is asked.

2c. HTTP cache

The HTTP cache is the browser's own store of files it has downloaded before, kept on disk and in memory. The browser follows the rules in the Cache-Control header (a response header that says how long a file may be reused and by whom):

  • Fresh (still inside max-age, the number of seconds a copy may be reused): the browser uses the saved copy at once. There is no network request.
  • Stale (too old), but has an ETag or Last-Modified: an ETag is an ID (a fingerprint) for one version of a file. Last-Modified is the date the file last changed. The browser sends one of them back and asks the server, "has this changed?" If not, the server sends a small 304 Not Modified answer with no body.

HTML is usually not cached for long. Files with a hash in the name, like main.a1b2c3.js, are often cached for a very long time (max-age=31536000, immutable, which means one year, and "this file will never change"). Lesson 05 covers this in depth.

2d. Back/forward cache (bfcache)

The back/forward cache (bfcache) is a browser feature that keeps a whole page frozen in memory when you leave it. When you press Back, the browser brings back that whole page from memory, including its JavaScript state, so the page appears at once. Some things can stop this. Two common ones are an unload event listener (code that runs when the page is closed) and Cache-Control: no-store. Browsers keep changing these rules, so test your pages in DevTools.

Step 3: DNS (name to IP address)

Computers connect to IP addresses, not names. DNS (the Domain Name System) is a worldwide service that turns a name into an IP address (the number that identifies a computer on the network, like 76.76.21.21). It works like a phone book. To do this, the browser checks a chain of caches. It starts with the closest one and moves to the farthest:

  1. Browser DNS cache
  2. OS cache (and the /etc/hosts file)
  3. Recursive resolver: a server that does the lookup for you. It is run by your ISP (Internet Service Provider, the company that gives you internet), or it is a public one like 1.1.1.1 or 8.8.8.8.
  4. If the resolver has no saved answer, it asks a root server ("who handles .dev?"). Then it asks the TLD server (TLD means top-level domain, the last part of a name, like .dev; "who handles react.dev?"). Last, it asks the authoritative server (the server that holds the official records for that domain: "react.dev is at 76.76.21.21").

Each answer has a TTL (time to live). The TTL says how long the answer may be saved. (The DevOps series covers this in its DNS lesson.)

Senior details:

  • A cold DNS lookup (nothing saved yet) can take tens to hundreds of milliseconds. A saved one takes almost no time.
  • <link rel="dns-prefetch" href="https://api.example.com"> is a hint in the HTML that tells the browser to look up that domain early. A third-party domain is one that is not yours.
  • Modern browsers may use DNS over HTTPS (DoH), which sends the DNS question inside an encrypted HTTPS connection, so others on the network cannot read it.
  • With a CDN, DNS often gives the IP of the nearest edge server (a CDN server close to the user), not your origin server (the server that holds the real, original content). The choice of location happens here.

Step 4: Connection (TCP or QUIC)

HTTP/1.1 and HTTP/2: TCP

TCP (Transmission Control Protocol) is the set of rules that makes data arrive complete and in order. It needs a three-way handshake (three small messages) before it can send any data. Think of it as two people saying "hello, can you hear me?", "yes, can you hear me?", "yes":

TEXT
Browser → SYN       → Server      (SYN = "let's synchronize")Browser ← SYN-ACK   ← Server      (the server agrees)Browser → ACK       → Server      (ACK = "acknowledged"; 1 round trip spent)

One round trip (RTT) is the time for a packet (a small piece of data) to reach the server and come back. Say a user in Ahmedabad connects to a server in the US. The RTT might be about 250 ms. Then the handshake alone costs 250 ms. This is why CDNs matter: they put the server physically closer to the user.

HTTP/3: QUIC over UDP

HTTP/3 is the newest version of HTTP. It runs on QUIC, a protocol that is built on UDP. QUIC does the connection handshake and the TLS handshake together. So a new connection is ready after about 1 RTT instead of 2.

QUIC also avoids TCP's head-of-line blocking. This means one stuck item at the front of a line holds up everything behind it. In TCP, one lost packet makes every stream (one request and its answer) on the connection wait. With QUIC, the other streams keep going.

Senior detail: connections are reused. HTTP/2 and HTTP/3 send many requests over one connection (this is called multiplexing). So you usually pay the handshake cost only once for each origin. An origin is the scheme, host and port together, like https://react.dev:443. Long ago, with HTTP/1.1, people spread files across many domains ("domain sharding") to get more connections. With HTTP/2 and HTTP/3 this trick hurts, because each new domain needs its own handshakes.

Step 5: TLS (the padlock)

TLS (Transport Layer Security) is a protocol that lets the browser and the server agree on secret keys and then encrypt (scramble) all their messages. HTTPS means HTTP carried inside TLS. With TLS 1.3 (the current version), it works like this:

  1. The browser says hello. It lists the ciphers (encryption methods) it supports, and it sends its key share (its half of the data that both sides mix to make the same secret key).
  2. The server answers with its own key share and its certificate (a digital ID card for the site, signed by a trusted organization).
  3. The browser checks the certificate. Is it signed by a trusted Certificate Authority (CA, an organization that browsers trust to confirm who owns a domain)? Is it valid for this domain? Has it expired?
  4. Both sides work out the same session keys (temporary secret keys for this one connection). Now encrypted HTTP can start.

TLS 1.3 needs 1 RTT (TLS 1.2 needed 2). A resumed session (a repeat visit) can use 0-RTT. This sends data at once, but an attacker can replay it (send it again). So 0-RTT is only safe for idempotent requests. An idempotent request is one that gives the same result if it runs twice, like reading a page.

Running total for a cold HTTPS visit over TCP: DNS + 1 RTT (TCP) + 1 RTT (TLS), all before the first byte of the request is sent. On a 250 ms RTT, that is 500 ms or more of pure setup.

Fix in the interview: <link rel="preconnect" href="https://cdn.example.com"> is an HTML hint that makes the browser do DNS + TCP + TLS early. Use it for an origin you know you will need.

Step 6: The HTTP request

Now the browser sends the real request:

HTTP
GET / HTTP/2Host: react.devUser-Agent: Mozilla/5.0 ...Accept: text/html,application/xhtml+xmlAccept-Encoding: gzip, br, zstdAccept-Language: en-IN,en;q=0.9Cookie: session_id=abc123

Senior details:

  • Cookies are added automatically for that domain. The SameSite, Secure and Domain rules decide which ones are sent. This is the root of CSRF (cross-site request forgery, an attack that tricks your browser into sending a request you did not mean to send). See Lesson 03.
  • Accept-Encoding tells the server which compression formats the browser can unpack. Compression makes a file smaller for the trip. gzip, Brotli (br) and zstd are three such formats.
  • For a navigation (moving to a new page), the browser also sends headers like Sec-Fetch-Mode: navigate. They tell the server what kind of request this is, and servers can use them for security decisions.

Step 7: Server work (the TTFB story)

The request rarely goes straight to "the server." A typical production path looks like this:

TEXT
Browser → CDN edge → (cache hit? return now) → Load balancer → App server        → (auth, business logic) → DB / Redis / external APIs → render HTML → back

A load balancer is a server that spreads requests across several app servers. An app server is the program that runs your code. Redis is a very fast database that keeps data in memory, often used as a cache. A DB is a database. A cache hit means the saved copy was found.

The time from sending the request to receiving the first byte of the answer is TTFB (Time to First Byte). It includes the network trip and all the server work.

Rendering here means building the HTML of a page. In a Next.js app, what happens depends on the rendering mode:

ModeWhat the server doesTTFB
Static (SSG, Static Site Generation)Sends HTML that was built earlier, from the CDNVery low
ISR (Incremental Static Regeneration)Sends saved HTML and rebuilds it in the background after a set timeLow
SSR (Server-Side Rendering, dynamic)Fetches data and runs the React render for every requestDepends on the slowest data call
Streaming SSRSends the page shell (the fixed outer layout) at once, then streams the restLow first byte, content arrives bit by bit

Senior detail: 103 Early Hints is an HTTP status code that lets a server send a first, early reply. It tells the browser, "start fetching these CSS files and fonts." It does this before the final answer is ready. This uses time that would otherwise be wasted.

Step 8: The response

HTTP
HTTP/2 200Content-Type: text/html; charset=utf-8Content-Encoding: brCache-Control: no-cacheSet-Cookie: theme=dark; Path=/; Secure; SameSite=LaxStrict-Transport-Security: max-age=31536000; includeSubDomainsContent-Security-Policy: default-src 'self'

The browser does these things:

  • It reads the status (the 3-digit result code, like 200 for OK). A 301 or 302 is a redirect ("go to this other URL"). It sends the browser back to Step 3 with the new URL. A chain of redirects is a hidden performance cost.
  • It unpacks the compressed body.
  • It saves any cookies.
  • It applies the security headers (HSTS, and CSP, which is Content Security Policy, a list of rules about which scripts and files the page may load).
  • It starts parsing at once. It does not wait for the whole HTML file. The HTML is streamed.

Step 9: Parsing (HTML to DOM, CSS to CSSOM)

Parsing means reading the code and turning it into a structure the browser can use.

Building the DOM

The HTML parser reads bytes, then characters, then tokens (pieces like "start of a <div>"), then nodes. At the end, it has the DOM (Document Object Model): the page as a tree of objects that JavaScript can read and change.

The preload scanner

Sometimes the main parser is busy or blocked. A preload scanner looks ahead in the raw HTML at the same time. It starts downloading <img>, <link> and <script> files early. This is a big reason why files named in the HTML load faster than files added later by JavaScript.

CSS is render-blocking

Render-blocking means the browser waits and does not paint yet. It will not paint until it has the CSSOM (CSS Object Model: all the style rules as a tree). Without styles, the page would flash unstyled content. So a large or slow CSS file delays the first paint.

JavaScript is parser-blocking (by default)

A plain <script src="app.js"> stops HTML parsing until the script is downloaded and run. Parser-blocking means the HTML parser must wait. The reason is that the script might call document.write (code that inserts HTML into the page while it is still being read) or read the DOM.

Script typeDownloadRunsBlocks parsing?Order kept?
<script>ImmediatelyImmediately when downloadedYesYes
<script async>In parallelAs soon as downloadedOnly while it runsNo
<script defer>In parallelAfter parsing ends, before DOMContentLoaded (the event that fires when the DOM is ready)NoYes
<script type="module">In parallelDeferred by defaultNoYes

Rule of thumb: use defer for app code that needs the DOM. Use async for independent scripts, like analytics.

Step 10: Rendering (the critical rendering path)

Now the browser has a DOM and a CSSOM. The critical rendering path is the chain of steps from code to pixels. It has four steps:

  1. Style: it combines the DOM and CSSOM into the render tree. This tree has only the visible nodes. A node with display: none is left out.
  2. Layout (also called reflow): it works out the exact position and size of every box.
  3. Paint: it fills in the pixels: text, colors, borders, images and shadows.
  4. Composite: it puts the painted layers together in the right order. This often happens on the GPU (the graphics chip, which is fast at drawing).

Key milestones:

  • FCP (First Contentful Paint): the time when the first text or image appears.
  • LCP (Largest Contentful Paint): the time when the largest visible item (often the hero image or the main heading) appears. This is a Core Web Vital (one of Google's key page-experience measures). A "good" LCP is 2.5 s or less. See Lesson 09.

Senior detail: animating transform and opacity can skip layout and paint. The browser only needs to composite. This is why they look smooth. Animating width, top or height forces layout on every frame. See Lesson 08.

Step 11: After the load (where React apps spend their time)

For a React or Next.js app, "the page is painted" is not the end:

  • Hydration is the step where React makes server-made HTML interactive. React downloads the JS bundle (the JavaScript file or files that hold your app code) and runs your components again in the browser. Then it attaches event handlers (functions that react to clicks and typing) to the HTML that the server made. Until hydration ends, the button is visible but not clickable.
  • Lazy resources: these are things that load later, only when needed. Examples are images below the fold (the part of the page you must scroll to see, marked with loading="lazy"), code-split chunks (parts of the JS bundle that are loaded on demand) and fonts.
  • API calls from useEffect (a React hook that runs code after the component shows) or React Query (a library that fetches and caches server data) start. They often cause more renders.
  • Interactivity: INP (Interaction to Next Paint) measures how long the page takes to show a result after a click, tap or key press. It replaced FID (First Input Delay) as a Core Web Vital in March 2024.

This is why "the HTML arrived fast but the app feels slow" is a classic senior scenario. See Scenario Debugging, Lesson 01.

Worked example: a cold visit to a Next.js e-commerce site from Ahmedabad

StepTime (approx.)What is happening
DNS40 msResolver lookup, CDN returns the Mumbai edge IP
TCP15 ms1 RTT to the Mumbai edge
TLS 1.315 ms1 RTT, certificate checked
Request to TTFB600 msCDN miss, request goes to the origin in Virginia, SSR waits on a product API
HTML download50 msStreamed and Brotli-compressed
CSS (blocking)120 msOne 80 KB stylesheet
FCPabout 850 msHeader and skeleton visible
Hero image400 msFound late because it is a CSS background
LCPabout 1.4 sProduct image painted
JS bundle and hydration900 ms400 KB of JS on a mid-range Android phone
Interactiveabout 2.3 s"Add to cart" really works

Where a senior would look:

  • The 600 ms TTFB. Cache the HTML at the edge, or stream it.
  • The hero image that is found late. Use an <img> with fetchpriority="high" (an attribute that tells the browser to fetch it early), or preload it (see Q5).
  • The 900 ms hydration. Send less JS, and use Server Components (React components that run only on the server and send no JS to the browser).

The numbers are only examples. The point is to break the time down by layer instead of saying "it's slow."

Where caching skips steps (summary)

CacheSkipsControlled by
bfcacheEverythingAvoid unload handlers and no-store (test in DevTools)
Service WorkerThe network entirelyYour service worker code
HTTP cache (fresh)DNS, TCP, TLS, request, serverCache-Control: max-age
HTTP cache (revalidate)Body downloadETag or Last-Modified, answered with 304
DNS cacheDNS lookupTTL
Connection reuseTCP and TLS handshakesHTTP/2 and HTTP/3, keep-alive
CDN cacheOrigin server workCache-Control: s-maxage, CDN rules
App cache (Redis)Database workYour backend

Common mistakes in interviews

  1. Starting at "DNS" and stopping at "server sends HTML." You skip the caches before the network. You also skip everything after the response. In React apps, most of the time the user waits is spent in those parts.
  2. Saying "the server responds" with no detail. Mention the CDN, TTFB, and what drives it.
  3. Saying JavaScript is "loaded at the end." Explain parser-blocking versus defer and async.
  4. Forgetting redirects. http to https to www can add a full extra round trip or more.
  5. Treating "loaded" as "usable." Talk about hydration and INP.
  6. No numbers. Say, "On a 250 ms RTT, the TCP and TLS handshakes cost about 500 ms before the first request byte." Numbers show seniority.

How to answer in an interview (the 2-minute version)

"First the browser decides if the input is a URL. Then it checks the shortcuts: HSTS to force HTTPS, a Service Worker, the HTTP cache, or bfcache for back navigation. If it must use the network, it finds the IP with DNS through the browser, OS and resolver caches. It opens a TCP connection (or QUIC for HTTP/3) and finishes a TLS 1.3 handshake. On TCP, this costs about two round trips. It sends the GET request with cookies attached. The request likely reaches a CDN and maybe an origin server. The time until the first byte is TTFB. As the HTML streams in, the parser builds the DOM, and a preload scanner fetches files early. CSS blocks rendering. Plain scripts block parsing unless they are defer or async. Then style, layout, paint and composite give the first paint and LCP. For a React app, hydration makes the page interactive, and we measure that with INP. When I debug, I break the time down by these layers in the DevTools network waterfall and the Performance panel."

Then pause and let the interviewer pick a layer to zoom into.

Interview questions

Walk me through what happens after pressing Enter.

Use the 11-step map above, and keep the order. Start with input handling. Then the shortcuts before the network (HSTS, Service Worker, HTTP cache, bfcache). Then DNS, TCP or QUIC, TLS, the request, server work, the response, parsing, rendering and, last, hydration. Mention the caches before DNS and hydration after paint. These are the two places where weak answers stop too early.

Where can caching short-circuit this flow?

The places are: bfcache, the Service Worker, the HTTP cache, the DNS cache, connection reuse, the CDN, and a server-side cache such as Redis. For the HTTP cache, a fresh copy means no request at all. A stale copy means a cheap 304. See the summary table above for what each one skips.

Why does a <script> in <head> delay first paint, and how do defer and async differ?

A classic script blocks HTML parsing until it is downloaded and run. So the DOM is not ready, and paint is delayed. defer downloads in parallel and runs in order after parsing ends. async downloads in parallel and runs as soon as it is ready, in no fixed order. Module scripts act like defer by default.

Scenario: TTFB is 1.8 s. What do you check first?

First, split network time from server time. Use DevTools (the Timing tab, "Waiting for server response") and server logs or traces. Then check these questions:

  • Is the CDN missing its cache? Look for x-cache or cf-cache-status headers.
  • Are there redirects?
  • Is SSR waiting on a slow API or database query?
  • Is it a serverless cold start (serverless means your code runs in short-lived functions managed by a cloud company; a cold start is the extra wait while a new function instance starts up)?
  • Is the origin far from the user?

Fixes: cache at the edge, stream the HTML, fetch data in parallel, add indexes or a cache layer, and move the region closer to users.

What is the difference between DNS prefetch, preconnect and preload?

All three are hints placed in the HTML <head>. dns-prefetch only looks up the domain. preconnect does DNS, TCP and TLS. preload actually downloads one specific file that the current page needs. In one line: dns-prefetch is the lightest, preconnect opens the connection, and preload fetches the file.

HTTP/2 vs HTTP/3: what changed and why does it matter?

HTTP/2 sends many requests over one TCP connection (multiplexing). But one lost packet still makes all of them wait. This is TCP head-of-line blocking. HTTP/3 uses QUIC over UDP. Its streams are independent, and the connection and TLS handshakes are combined. So setting up a connection is faster.

The HTML arrived in 200 ms, but users cannot click "Buy" for 3 seconds. Why?

Maybe the JS bundle is too big. Maybe hydration is slow on a weak CPU. Or long tasks may be blocking the main thread. The main thread is the single thread in the browser that runs your JavaScript and also handles clicks and painting. A long task is a piece of work that keeps it busy for more than 50 ms. To fix it, send less JS (code splitting, Server Components), delay scripts that are not critical, and break up long tasks. Measure with INP and the Performance panel.

What does the browser check in a TLS certificate?

It checks four things. The certificate must lead back to a trusted Certificate Authority. It must match the domain. It must be inside its valid dates. And it must not be revoked (cancelled).

Key takeaways

  • The journey has network costs (DNS, TCP, TLS, TTFB) and rendering costs (parsing, CSS blocking, JS, layout, hydration). Seniors cover both.
  • Caching happens at almost every layer. Name them.
  • Round trips are the hidden cost. CDNs, connection reuse, HTTP/3 and preconnect all exist to cut them.
  • CSS blocks rendering, and JavaScript blocks parsing. Know defer vs async well.
  • For React apps, "painted" is not "interactive." Hydration and INP finish the story.
  • In interviews, break the time down by layer and use numbers.

What's next

Lesson 01: Storage in the Browser. We zoom into one header from Step 6, the Cookie header. Where does browser data live (cookies, localStorage, sessionStorage, IndexedDB), and who can read it?

Published by Jainik · Last updated · How these lessons are made

Keep learning

Related lessons

Related series