Back to Browser Internals
Browser Internals
easy
mid

What happens in the browser between a button click and the network request leaving the machine?

Click → DOM event dispatched (capture→target→bubble) → handler runs on the main thread → validation/state updates → fetch() called → browser builds the request, applies CORS/credentials/CSP checks, may do a preflight → DNS lookup → TCP handshake → TLS handshake → HTTP request bytes sent. The JS handler returns long before the response; the response callback is queued as a microtask.

7 min read·~12 min to think through

This question traces a single user action all the way down the stack — it tests whether you understand the browser event model, the JS runtime, and the network layer as one continuous pipeline.

Phase 1 — The DOM event

  1. User clicks "Pay Now". The browser does hit testing to find the target element.
  2. A click event is dispatched and travels in three phases: capture (window → target), target, bubble (target → window). Listeners fire along the way.
  3. The event handler is a macrotask — it runs on the main thread, on the call stack.

Phase 2 — The JS handler runs

  1. Your handler executes synchronously: validate the form, maybe e.preventDefault(), update UI state (disable the button, show a spinner), read the amount/token.
  2. It calls fetch("/api/pay", { method: "POST", body, headers }).
  3. fetch returns a pending Promise immediately. The handler finishes and the call stack unwinds — the network request has not gone out yet from JS's perspective; JS just handed it to the browser.

Phase 3 — The browser prepares the request

  1. The browser's networking stack takes over (off the main thread):
  • Builds the HTTP request (method, headers, cookies if credentials allows, body).
  • CORS check: for a cross-origin, non-simple request, it sends a preflight OPTIONS first and waits for Access-Control-Allow-* headers.
  • CSP / mixed-content checks — is this origin allowed by connect-src? Is it HTTPS?
  • Service worker fetch event may intercept here.
  • Cache check (could short-circuit).

Phase 4 — The request leaves the machine

  1. DNS resolution — hostname → IP (cached at OS/browser level if seen recently).
  2. TCP handshake — SYN / SYN-ACK / ACK (skipped if a connection is reused/pooled).
  3. TLS handshake — for HTTPS: certificate exchange, key negotiation (1–2 RTTs, or 0-RTT with TLS 1.3 resumption).
  4. The HTTP request bytes are written to the socketnow the request has truly "left the browser."

Phase 5 — Coming back (briefly)

  1. Response arrives → browser parses headers, re-checks CORS → resolves the fetch Promise.
  2. The .then callback is scheduled as a microtask and runs when the stack is next empty — updating the UI ("Payment successful").

What separates a strong answer

  • Knowing the event phases (capture/target/bubble).
  • Knowing fetch is async — the handler returns long before anything hits the network.
  • Naming CORS preflight, CSP, service worker as gates before the network.
  • The DNS → TCP → TLS → HTTP ordering, and that connection reuse skips steps.
  • The response callback is a microtask.

Senior framing

The senior version connects three mental models that are usually taught separately — the DOM event model, the event loop, and the network stack — into one timeline, and notes the async seam: the synchronous JS handler ends, the browser does the slow work, and the result re-enters JS as a microtask. Bonus: mention idempotency keys for the payment so a double-click doesn't double-charge.

Follow-up questions

  • What triggers a CORS preflight and what makes a request 'simple'?
  • How does connection pooling / keep-alive change this timeline?
  • How do you prevent a double-click from sending two payment requests?
  • Where could a service worker intercept this?

Common mistakes

  • Thinking the network request goes out synchronously inside the handler.
  • Skipping the CORS preflight / CSP checks.
  • Forgetting DNS/TCP/TLS happen before any HTTP bytes are sent.
  • Not mentioning the response callback is a microtask.

Edge cases

  • Reused keep-alive connection skips DNS/TCP/TLS entirely.
  • TLS 1.3 0-RTT resumption sends data on the first flight.
  • A service worker can fully intercept and serve from cache — no network at all.
  • Double-submit needs button disabling + an idempotency key server-side.

Real-world examples

  • Checkout/payment flows, form submissions, any fetch triggered by a click.

Related questions