What Happens When You Type a URL in the Browser? - Part 1: From URL to HTTP Response

You type a URL into the browser, press Enter, and a few seconds later, a page appears on the screen.

For frontend engineers, it is easy to think that an application starts when JavaScript begins to run. But the browser starts working long before that.

Before the first component is hydrated, before the application’s first fetch() call runs, and before the main bundle starts executing, the browser may already have parsed the URL, checked caching mechanisms, resolved DNS, established a connection, negotiated security, sent an HTTP request, and started receiving a response.

This post is the first part of a series about the journey from a URL to the pixels displayed on the screen. In this part, we will follow the process up to the HTTP response:

URL
→ cache / service worker
→ DNS
→ connection
→ HTTP request
→ CDN / server
→ HTTP response

This is a simplified model. HTTP/1.1 and HTTP/2 commonly run over TCP and TLS, while HTTP/3 uses QUIC over UDP and changes some of the connection-establishment details.

The goal is to understand the process through the Web Platform itself: URLs, origins, caching, service workers, DNS, connections, TLS, HTTP, headers, and DevTools. Frameworks run on top of these foundations, and understanding them makes performance, security, and loading problems much easier to investigate.


1. Parsing the URL and Determining the Origin

Suppose you navigate to:

https://example.com/products?id=123#reviews

The browser does not treat this as an arbitrary string. It parses the URL into components:

scheme/protocol: https
hostname:        example.com
pathname/path:   /products
query/search:    ?id=123
fragment/hash:   #reviews

The scheme, exposed as protocol by the JavaScript URL API, defines how the resource should be accessed. For a typical web page, this is usually HTTPS.

The hostname identifies the domain name or host address. The pathname identifies the path within that origin. The query string, exposed as search, carries additional parameters, while the fragment, exposed as hash, points to a location or state within the document.

One important detail: the fragment is normally not sent to the server as part of the HTTP request. It is handled by the browser on the client side.

From the URL, the browser can also determine its origin. An origin is defined by the combination of scheme, host, and port.

For example:

https://example.com
http://example.com

These are different origins because the schemes are different.

The same is true here:

https://example.com
https://api.example.com

The schemes are the same, but the hosts are different.

And here:

https://example.com
https://example.com:8443

The scheme and host are the same, but the ports are different. Default ports are normalized when comparing origins, so https://example.com and https://example.com:443 are considered the same origin.

Origin is one of the most important concepts in browser security. Storage, service workers, permissions, CORS, and the Same-Origin Policy all depend on origin boundaries in different ways.

You can inspect URL components directly in JavaScript:

const url = new URL("https://example.com/products?id=123#reviews");

console.log(url.protocol); // "https:"
console.log(url.hostname); // "example.com"
console.log(url.host); // "example.com"
console.log(url.pathname); // "/products"
console.log(url.search); // "?id=123"
console.log(url.hash); // "#reviews"
console.log(url.origin); // "https://example.com"

It is also useful to understand the difference between host and hostname:

const url = new URL("https://example.com:8443/products");

console.log(url.host); // "example.com:8443"
console.log(url.hostname); // "example.com"
console.log(url.port); // "8443"

host includes the port when one is present. hostname contains only the domain name or host address.


2. Before the Network: Cache and Service Workers

After parsing the URL, the browser needs to determine how to obtain the resource. A network request may be necessary, but not every navigation or resource load starts from zero.

Browsers have several caching layers and related mechanisms, including:

  • HTTP cache;
  • memory and disk caches;
  • back-forward cache, or bfcache;
  • Cache API;
  • service workers.

These are not necessarily checked in one fixed universal order. Their behavior depends on the browser, resource type, navigation, request mode, caching headers, and current page state.

The HTTP cache is governed primarily by HTTP caching rules and response headers such as Cache-Control, ETag, and Last-Modified.

Cache-Control: max-age=3600
ETag: "abc123"
Last-Modified: Wed, 01 Jul 2026 12:00:00 GMT

The bfcache is different. Instead of caching individual network responses, it can preserve an entire page in memory so the browser can restore it quickly when the user navigates backward or forward.

The Cache API is programmable storage for request-response pairs. It is frequently used together with service workers.

A service worker is not simply another cache. It is a programmable worker that can intercept requests within its scope and decide how to respond: from the network, from the Cache API, or through another strategy.

A service worker also does not automatically intercept the very first visit to a site. To control a navigation, it generally needs to have been installed, activated, and taken control of the page. A genuine first visit will normally still require the network.

A basic cache-first service worker might look like this:

const CACHE_NAME = "app-shell-v1";
const ASSETS = ["/", "/styles.css", "/main.js"];

self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS)),
  );
});

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cachedResponse) => {
      return cachedResponse || fetch(event.request);
    }),
  );
});

This strategy is called cache first because it checks the cache before falling back to the network.

Caching can make repeat visits dramatically faster, but incorrect caching can also cause stale HTML, outdated assets, broken deployments, and inconsistent behavior between users. That is why understanding where a response came from is an important part of frontend debugging.


3. DNS: Finding Where to Connect

If the browser needs the network, it must determine where the target server can be reached.

Humans usually work with domain names such as example.com. Network communication ultimately needs an IP address.

DNS, the Domain Name System, maps domain names to information such as IPv4 and IPv6 addresses.

A simplified resolution flow looks like this:

browser / OS cache
→ recursive DNS resolver
→ root servers
→ TLD servers
→ authoritative nameserver
→ IP address

The browser does not usually contact every server in this hierarchy itself. It may check local caches and then rely on the operating system or a configured DNS resolver. The recursive resolver may already have the answer cached. If it does not, it can query the DNS hierarchy on the client’s behalf.

The root servers help locate the servers responsible for a top-level domain such as .com or .org. The TLD servers then direct the resolver toward the authoritative nameserver responsible for the requested domain.

In practice, caching means many lookups are resolved without traversing the full hierarchy.

Why DNS matters to frontend engineers

DNS adds work before a browser can connect to a new hostname. If a page depends on many external origins, such as analytics, fonts, image hosts, APIs, and third-party widgets, the browser may need additional DNS resolutions and connections.

Each external origin can introduce costs related to:

  • DNS resolution;
  • connection establishment;
  • TLS negotiation;
  • resource prioritization;
  • security policies;
  • third-party availability.

Resource hints can help when used deliberately:

<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

dns-prefetch suggests that DNS resolution should happen early. preconnect goes further and asks the browser to prepare a connection to the origin ahead of time, potentially including TLS negotiation for HTTPS.

These hints should be reserved for origins that are genuinely important to the initial page load. Too many preconnections can waste resources and compete with more critical work.


4. Establishing the Connection

Once the browser knows where to connect, it needs a transport connection before it can exchange HTTP messages.

For HTTP/1.1 and HTTP/2 over HTTPS, that commonly means TCP followed by TLS.

TCP

TCP, or Transmission Control Protocol, provides reliable, ordered communication between endpoints. Before application data can be exchanged, the client and server establish a connection using the three-way handshake:

Client → SYN     → Server
Client ← SYN/ACK ← Server
Client → ACK     → Server

The exact protocol details are deeper than most frontend engineers need in daily work. The important performance insight is that establishing a new connection requires network round trips.

The farther the user is from the server, the more expensive those round trips become. This is one reason CDNs, edge servers, connection reuse, and modern HTTP versions can improve loading performance.

TLS

If the page uses HTTPS, the client and server also establish a secure channel using TLS, or Transport Layer Security.

TLS provides three properties that matter directly to the Web:

  • authentication — the browser can validate the server’s identity;
  • confidentiality — traffic is encrypted;
  • integrity — modification of traffic in transit can be detected.

During TLS negotiation, the client and server agree on cryptographic parameters, validate the server certificate, and establish keys for protected communication.

HTTPS is not an optional performance trade-off. It is a fundamental security requirement for the modern Web, and many Web Platform features require a secure context.

What changes with HTTP/3?

HTTP/3 does not use TCP. It runs over QUIC, which itself uses UDP and incorporates transport reliability along with TLS 1.3 into the QUIC connection process.

So this mental model:

DNS
→ TCP
→ TLS
→ HTTP

is useful for HTTP/1.1 and HTTP/2, but it is not an exact representation of HTTP/3.


5. Sending the HTTP Request

Once the connection is ready, the browser can ask the server for the document.

A simplified HTTP/1.1 navigation request might look like this:

GET /products?id=123 HTTP/1.1
Host: example.com
Accept: text/html
Accept-Language: en-US
Accept-Encoding: gzip, br, zstd

The method GET indicates that the client wants to retrieve a representation of a resource.

The request target /products?id=123 contains the pathname and query string. Notice that the fragment from the original URL, such as #reviews, is not included.

Request headers provide additional context. Accept describes the media types the client can handle. Accept-Language communicates language preferences. Accept-Encoding lists supported content encodings. Cookies that apply to the request may also be sent in a Cookie header.

HTTP/2 and HTTP/3 encode requests differently from HTTP/1.1. Instead of the traditional request-line representation, they use pseudo-headers such as:

:method: GET
:scheme: https
:authority: example.com
:path: /products?id=123

The semantics remain familiar even when the wire representation changes: the browser identifies the method, target origin, path, and request metadata, then sends that information to the server.


6. CDN, Edge, and Origin

The request does not necessarily travel directly to the server that generated the application.

Many sites place a CDN, or Content Delivery Network, between users and the origin server.

A simplified path might be:

Browser
→ CDN edge
→ origin server

An edge server is positioned geographically closer to users than the origin may be. If it already has a valid cached response, it can return it without contacting the origin:

Browser
→ CDN edge
→ cached response

If the CDN does not have a usable cached response, a cache miss occurs and the edge may fetch the resource from the origin:

Browser
→ CDN edge
→ origin server
→ CDN edge
→ Browser

This is especially valuable for static files such as CSS, JavaScript, images, and fonts. Dynamic HTML can also be cached at the edge when the application’s caching strategy allows it.

This part of the journey is closely related to TTFB, or Time to First Byte: the time from the start of the navigation or request measurement until the first byte of the response becomes available to the browser.

A high TTFB can have many causes, including:

  • network latency;
  • DNS and connection setup;
  • redirects;
  • CDN cache misses;
  • overloaded servers;
  • expensive server-side rendering;
  • slow database queries;
  • calls to external services.

TTFB is a signal, not a diagnosis. It tells you that the browser waited, but not automatically why.


7. Receiving the HTTP Response

After processing the request, the server returns an HTTP response.

A simplified HTTP/1.1 response might look like this:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=3600
Content-Encoding: br
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax

<!doctype html>
<html lang="en">
  <head>
    <title>Example</title>
  </head>
  <body>
    <h1>Hello</h1>
  </body>
</html>

At a high level, an HTTP response contains three things:

  1. status — what happened;
  2. headers — metadata and instructions;
  3. body — the actual content.

Common status codes include 200 OK, redirects such as 301 and 302, 304 Not Modified, client errors such as 404 Not Found, and server errors such as 500 Internal Server Error.

For frontend engineers, a few response headers are especially important.

Content-Type

Content-Type tells the browser what kind of data it received, for example text/html, text/css, application/json, or image/png.

An incorrect content type can cause a resource to be interpreted incorrectly or rejected entirely.

Cache-Control

Cache-Control defines caching behavior.

A versioned static asset might use a policy such as:

Cache-Control: public, max-age=31536000, immutable

This works well for filenames that change whenever the content changes, such as /assets/main.8f3a2.js.

HTML usually requires a more careful strategy because stale HTML can reference assets that no longer exist after a deployment.

Validators such as ETag and Last-Modified can also allow the browser to revalidate cached content and receive a 304 Not Modified response when the resource has not changed.

Content-Encoding

Content-Encoding indicates that the response body has been encoded, commonly with gzip, br (Brotli), or zstd.

Compression can significantly reduce transferred bytes for text-based resources.

Set-Cookie allows a server to store cookies in the browser. Attributes such as HttpOnly, Secure, SameSite, Path, Domain, Max-Age, and Expires control how those cookies behave.

Security headers such as Content-Security-Policy and cross-origin response headers are also important. CORS, in particular, is enforced by the browser: when the required cross-origin permissions are missing, JavaScript may be prevented from reading a response. For requests that require a preflight, the browser may first send an OPTIONS request and refuse to send the main request if that preflight fails.

The response body for a document navigation is usually HTML. Once the browser has that HTML, the next stage begins: parsing the document, discovering additional resources, building rendering structures, and eventually painting pixels on the screen.


8. Seeing the Process in DevTools

The most useful place to observe this journey is the Network panel in browser DevTools.

Try this exercise:

  1. Open a private or incognito window.
  2. Open DevTools.
  3. Go to the Network panel.
  4. Enable Disable cache if you want to observe a cold network load.
  5. Navigate to a website.
  6. Select the main document request.
  7. Inspect Timing.
  8. Inspect the request and response Headers.

Then ask:

  • Did the request redirect?
  • Did the response come from a cache?
  • Which protocol was used?
  • How long did DNS and connection setup take?
  • What was the TTFB?
  • Which Content-Type was returned?
  • Which Cache-Control policy was applied?
  • Was the response compressed?
  • Were cookies sent or set?
  • Were security headers present?

You can also inspect parts of the navigation programmatically with the Navigation Timing API:

const [navigation] = performance.getEntriesByType("navigation");

if (navigation) {
  console.table({
    dns: navigation.domainLookupEnd - navigation.domainLookupStart,
    connection: navigation.connectEnd - navigation.connectStart,
    tls:
      navigation.secureConnectionStart > 0
        ? navigation.connectEnd - navigation.secureConnectionStart
        : 0,
    requestToFirstByte: navigation.responseStart - navigation.requestStart,
    responseDownload: navigation.responseEnd - navigation.responseStart,
  });
}

Some values may be zero because a step was unnecessary, reused an existing connection, or is exposed differently by the browser. The point is not to memorize every field, but to connect what DevTools shows you with the underlying loading process.


9. Why Frontend Engineers Should Care

This entire journey happens before the browser can fully process and render the application.

That means many problems that appear to be “frontend performance” problems may actually happen before JavaScript becomes relevant.

A slow page might be caused by:

  • an unnecessary redirect;
  • slow DNS resolution;
  • too many external origins;
  • repeated connection setup;
  • a CDN cache miss;
  • high server processing time;
  • poor caching rules;
  • a large uncompressed HTML response.

The same applies to security and debugging.

A CORS error is not a React error. A CSP violation is not a bundler error. A stale deployment may be a caching problem. An incorrect Content-Type may cause the browser to reject a perfectly valid file. A cookie problem may come from SameSite, Secure, domain, or path rules rather than application state.

Frameworks can generate HTML, configure headers, define routes, run middleware, optimize assets, and integrate with CDNs. But the browser still works with URLs, origins, HTTP requests, HTTP responses, caching rules, and security policies.

When something is slow or broken, “the framework is slow” is rarely a useful diagnosis by itself. Open the Network panel and identify where the time or failure actually occurs.


Conclusion

When you type a URL into the browser, a surprising amount of work happens before the application can render anything.

The browser parses the URL and determines its origin. Existing caches or a service worker may satisfy the request without a full network trip. Otherwise, DNS resolves the hostname, the browser establishes a connection, HTTP carries the request through infrastructure such as a CDN, and the server returns a response containing a status, headers, and a body.

A useful mental model is:

URL
→ local browser mechanisms
→ DNS
→ connection
→ HTTP request
→ CDN / origin
→ HTTP response

Understanding this path makes it easier to reason about loading performance, caching bugs, security policies, deployment issues, and unexpected browser behavior.

And this is only the first half of the story.

In the next part, the browser has received the HTML and needs to transform it, together with CSS and JavaScript, into something the user can see:

HTML
→ DOM
→ CSSOM
→ render tree
→ layout
→ paint
→ composite
→ pixels on the screen

  • WHATWG URL Standard
  • WHATWG Fetch Standard
  • MDN: URL API
  • MDN: Same-Origin Policy
  • MDN: Service Worker API
  • MDN: Cache API
  • MDN: PerformanceNavigationTiming
  • MDN: Cross-Origin Resource Sharing (CORS)
  • MDN: Content Security Policy (CSP)
  • RFC 1034 and RFC 1035: DNS
  • RFC 8446: TLS 1.3
  • RFC 9110: HTTP Semantics
  • RFC 9111: HTTP Caching
  • RFC 9112: HTTP/1.1
  • RFC 9113: HTTP/2
  • RFC 9114: HTTP/3
  • RFC 9293: TCP
  • Chrome DevTools Network panel documentation