2025/03/18
Anatomy of a Fetch request
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);
}
Blink
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
URLLoaderinstance - Create a
URLRequestinstance - 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