2025/03/18

Share

on:

Anatomy of a Fetch request

share

This article stems from one of my favourite interview questions: “Can you explain how a fetch request works?”. It’s not just about the specific subject of the question, but about how intentional the approach to the understanding of the inner workings of a technology is. I believe that understanding how things work is a key part of being a good technical professional, and being intentional about it is what makes the difference between a good and a great one.

Foreword: we’re gonna be using the term “browser” throughout this article, but only Chromium-based browsers (like Chrome, Edge, Opera, Brave, etc.) are covered. The concepts are the same for other browsers, but the implementation details may vary.

Let’s dive into the anatomy of a fetch request, and see how it works under the hood. We’ll start from a very simple request:

fetch("https://example.com");

We know fetch is a global function in the window object. So where does it come from?

IDL

The fetch function is defined in the Fetch API IDL. The IDL (Interface Definition Language) is a language used to define the interfaces and types used in a web API. The IDL for the Fetch API defines the fetch function, its parameters, and its return value.

Its contract is defined as such:

[Exposed=(Window,Worker), SecureContext]
partial interface WindowOrWorkerGlobalScope {
  Promise<Response> fetch(RequestInfo input, optional RequestInit init);
}

The function lives in the renderer process of the browser. The renderer process is one of the main processes in a browser, responsible for rendering web pages and, most importantly in this case, executing JavaScript code. The renderer process exposes fetch function and, when called, it sends a message to the network process, which handles the actual network requests and responses, via IPC (Inter-Process Communication), implemented in the NetworkService class using Mojo: an IPC library collection used in Chromium.

In Blink, the FetchManager class will start the process of creating a fetch request using the FetchManager::Fetch() method.

Once a ResourceRequest (low-level representation of a network request) is prepared, Blink sends it via Mojo IPC to the Network Service, which is responsible for handling network requests and responses, via the network::mojom::NetworkContext interface, used to create a
network::mojom::URLLoaderFactory:

network::mojom::URLLoaderFactory::CreateLoaderAndStart(...)

The payload sent over will include every single bit of information regarding the request, such as: • Method • Headers • Body • Credentials • Referrer • Priority Etc..

Network service

The Network service is a separate process in the browser that handles all network requests.

The top-level network stack object is the URLRequestContext. The context has non-owning pointers to everything needed to create and issue a URLRequest. The context must outlive all requests that use it.

It passes a network::ResourceRequest object and network::mojom::URLLoaderClient Mojo channel to the network::mojom::URLLoaderFactory, and tells it to create and start a network::mojom::URLLoader.

The URLLoader then creates a URLRequest to drive the network request. That job first checks the HTTP cache, and then creates a network transaction object, if necessary, to actually fetch the data. That transaction tries to reuse a connection if available. If none is available, it creates a new one. Once it has established a connection, the HTTP request is dispatched, the response read and parsed, and the result returned back up the stack and sent over to the caller.

The steps to load a resource are as follows:

  • Create a URLLoader instance
  • Create a URLRequest instance
  • Set the request URL
  • Set the request method, headers, body, credentials, etc.
  • Resolve the DNS
  • Handle proxy rules
  • Apply throttling
  • Check/apply http cached responses
  • Create an URLRequest
  • Initiate a socket connection to the remote server
  • Handle the TCP handshake (socket(), connect(), etc.)
  • Handle the TLS handshake (if applicable), with Borings SSL
  • Send the request to the server

Back to Mojo

As the response comes back, the Network service will stream it back to the renderer process via Mojo IPC. Blink will waste no time accumulating the response, and wrap it in a ReadableStream object, which is a standard web API that allows you to read data from a stream of bytes.

The response

The ReadableStream object is then passed to the Response constructor, which is responsible for creating a Response object from the stream. The Response object is a standard web API that represents the response to a network request.

Cherry on top

After the hard work is done, Chrome might cache the response if its headers indicate that it should. It will then update performance metrics and notify

Bibliography