lumo

Module 01 · Lesson 2 of 14Free

Request lifecycle: what happens when you open a website

Level
Beginner
Topic
System design fundamentals
Reading time
15 min

When you finish this page you can

  1. Trace a product-page request from a phone through the network, application and database.
  2. Explain why server response time differs from the time before a page becomes visible.
  3. Identify which work caching and connection reuse can skip.
  4. Locate a likely failure from the last successful step.
  1. Introduction to system design13 min

Video lesson · coming soon

Asha opens a product page on from her phone. In this lesson’s example, the application takes 95 ms, but the product text appears after 400 ms.

Where did the rest of that time go? We’ll follow one request from the address bar to the screen.

Check yourselfAnswer in your head first, then reveal
Kartly records no application request when Asha sees an error. Does that prove her phone never contacted Kartly?Show answerHide answer
No. Her phone might have contacted Kartly’s public entry point but failed before the request reached the application. Application logs cover only one part of the journey.

Diagram not drawn yet: request-lifecycle-kartly.svg

The phone first asks DNS for Kartly’s address. It then establishes TCP and TLS with the public entry point, which forwards HTTP to one app server. The app reads PostgreSQL and calls the pricing service. The response returns through the entry point to the phone, where the browser renders it.

Finding an address, establishing a connection, running application code and drawing the page are separate jobs.

Kartly is still at its one-server stage: one app server with eight CPU cores and one PostgreSQL database. The public entry point below is a forwarding role, not a second app server.

We’ll trace over . Other transports come later; first, we need a clear path to follow.

Find the address before connecting

Asha enters https://kartly.example/products/482.

For our request, https selects secure communication. kartly.example identifies the host. /products/482 identifies the product resource.

, the Domain Name System, finds an IP address for the hostname. A cached answer can avoid a remote lookup. Otherwise, the browser’s lookup reaches a DNS resolver, a service that obtains the answer. DNS resolves the hostname, not the product path.1

Assume Kartly’s answer is 203.0.113.10. This documentation address represents its public entry point, not its database.

Our scenario assigns 40 ms to the lookup. That is an illustrative duration, not a normal value for every phone.

Notice the boundary: knowing an address does not prove anything is listening there. If someone gives you a building’s address, the building can still be closed.

Asha now knows where to connect. She has not requested the product yet.

Establish a protected conversation

TCP establishes a connection through three messages: SYN, SYN-ACK and ACK. then authenticates the server and establishes before the request. These are connection setup steps, not product-page work.1

Think of the different questions they answer:

  • DNS: where should the phone connect?
  • TCP: can these establish a connection?
  • TLS: can the phone establish a protected conversation with the intended server?

For this scenario, assign 60 ms to TCP setup. Assign another 60 ms to TLS setup. These are budget assumptions, not universal handshake timings.

Suppose the public entry point is reachable, but its is invalid. Our expected outcome is a browser security warning. Making the product query faster would not help: application code has not run.

We choose to stop at that warning rather than bypass verification. A product page that loads through an untrusted connection would defeat the protection HTTPS is meant to provide.

Connection setup is now complete. Only now does the browser ask for the product.

The request reaches application code

HTTP carries the request and response. Here is a readable HTTP/1.1 example; headers carry information about the message, and the path identifies the requested resource.1

http
GET /products/482 HTTP/1.1
Host: kartly.example
Accept: text/html

At Kartly’s public entry point, a forwards requests to the application. A uses this forwarding role to distribute requests across available app servers. NGINX supports forwarding to a single destination or a server group.2

Kartly currently has only one app server, so there is no distribution choice. For our example, the entry point also handles the phone’s TLS connection.

The distinction matters: the phone’s connection ends at the entry point. Forwarding to the app is another network hop. Encryption on that internal hop is a separate configuration choice.

Here is the request lifecycle inside this scenario:

  1. The entry point forwards the product request to the app.
  2. The app selects the handler for /products/482.
  3. The handler asks PostgreSQL for product 482.
  4. PostgreSQL returns the stored product fields.
  5. The app asks the pricing service for the current price.
  6. The app combines those results into HTML.
  7. The entry point forwards the response to the phone.

The pricing service is an existing dependency in Kartly’s example world. We are not introducing a new service to solve this request.

Each component has a boundary

This is the responsibility map for our design:

Component Responsibility Talks to State it keeps Failure consequence
Phone browser Request and display the page DNS, entry point Cached data, connections Error or incomplete screen
DNS resolver Obtain the hostname’s address DNS servers Cached answers No usable destination
Public entry point Accept and forward HTTP Phone, app Connections, routing configuration App may receive nothing
App server Assemble the product page Database, pricing service Current request data Page cannot be assembled
PostgreSQL Store product records App Persistent product data Product lookup fails
Pricing service Supply the current price App Pricing data Current price is unavailable

This page is a read path. Opening it does not create an order or change stock. That keeps a failed page load separate from a failed purchase.

The server has assembled a response, but the customer still needs something visible.

A response is not a screen

Our application returns HTML through the entry point:

http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

<!doctype html>
<html lang="en">
<head><title>Desk lamp</title></head>
<body><h1>Desk lamp</h1><p>₹799</p></body>
</html>

turns downloaded content into visible pixels. The browser parses HTML, applies styles, calculates positions and paints. Images, stylesheets and scripts can require further requests; parsing can begin before the complete HTML arrives.1

For our calculation, the small HTML response contains everything needed to show the initial product text. A product photograph can appear later. This assumption keeps “text visible” separate from “every resource finished”.

Imagine two versions of Kartly:

  • Version A returns the product text in HTML.
  • Version B returns an empty page, then requires JavaScript to fetch the product.

In version B, our product fetch becomes another dependency before text can appear. That may suit an application with complex client-side behaviour, but it adds work to this particular path.

A successful HTML response therefore proves less than a successful customer experience. We need to account for the whole path.

Account for the missing milliseconds

These numbers are an illustrative budget for Asha’s first visit. They are not measured production statistics.

Stage Added time
DNS lookup 40 ms
TCP setup 60 ms
TLS setup 60 ms
HTTP request travels to entry point 30 ms
Entry-point forwarding overhead 1 ms
Application, including dependencies 95 ms
First response bytes travel back 30 ms
Remaining HTML transfer 24 ms
Initial text rendering allowance 60 ms

The application’s 95 ms follows Kartly’s established example: 5 ms of CPU work, 40 ms waiting for PostgreSQL and 50 ms waiting for pricing.

Do not add those dependency waits again. They are already inside application time.

Reach the first response byte

40 + 60 + 60 + 30 + 1 + 95 + 30 = 316 ms

The application contributes only part of the wait. Connection setup and travel happen outside its timing.

Finish receiving the HTML

316 ms + 24 ms = 340 ms

Receiving the first byte and receiving the complete document are different milestones.

Show the initial product text

340 ms + 60 ms = 400 ms

This simplified budget places rendering afterward. Actual parsing and downloading can overlap, so this is not a browser scheduling formula.

Now suppose you halve application time. Under these assumptions, you save about 48 ms—not half the page-load time.

That calculation tells you where further investigation belongs. It also raises another question: must every visit pay every cost?

The next request can skip work

Caching means retaining a result for reuse. Connection reuse means retaining the conversation itself. They save different work: a cached address avoids lookup, while an existing connection avoids setup. curl also reuses connections across transfers within one invocation.1

Diagram not drawn yet: request-lifecycle-reuse.svg

Two panels compare a fresh connection with an existing connection. The fresh path performs DNS, TCP and TLS before HTTP. The existing-connection path sends a new HTTP request without those setup steps. Both still reach the app for an uncached product response.

Reusing a connection skips setup, not the application work needed for a new response.

For our second request, assume the connection is still usable. Keep every other budget item unchanged.

That removes 160 ms of setup from the example. Initial text would appear after 240 ms. The product still comes from the application.

A different optimization is to reuse a stored response. For Kartly, that creates a product question: how old may the displayed price be?

How should Kartly reduce repeat-visit delay without accidentally treating an old price as current?

Always establish a fresh connection.

Each request repeats setup. In our budget, that adds 160 ms without making the product data fresher.

Reuse the complete product response without checking its age.

A stored product page avoids more work, but our customer may see yesterday’s price unless reuse has an explicit freshness policy.

Why this choice

The simplest improvement for this scenario avoids repeating setup without changing where the price comes from.

Response caching saves more work, but it answers a different question: when is old content acceptable? A static help page and a changing price need different policies.

Our choice changes if the product team accepts a bounded delay in displayed prices. Then cached product responses become an option. Checkout must still validate the purchase price.

Connection reuse also has a limit: if the connection closes, the next request must establish another one. It is an opportunity, not a guarantee.

Cache placement develops response caching. For now, keep the two questions separate: “Can we reuse the connection?” and “Can we reuse the answer?”

Find the last successful boundary

Use the following as a diagnosis exercise for our design. Symptoms are possible outcomes, not exact browser messages or universal status-code mappings.

Failure What Asha may see Evidence to seek Recovery action
DNS lookup fails Site cannot be found Lookup result; no connection attempt Restore the DNS answer or resolver access
TCP cannot connect Loading, then connection error Connection failure; no HTTP request Restore routing or the listener
TLS verification fails Security warning Certificate verification error Repair certificate configuration
Entry point cannot reach app Gateway error Forwarding error in entry-point logs Restore the app or its route
App handler fails Error page App error for this request Fix or roll back the failing change
Database or pricing call fails Error page, or missing price if designed Dependency error and duration Restore the dependency; show an explicit unavailable state
Response transfer breaks Partial page or network error Bytes sent versus received Investigate the connection interruption
Rendering or script work fails Incomplete or unresponsive page Browser errors and performance recording Fix the client resource or code

A proxy can enforce connection and read timeouts. NGINX documents these separately; its read timeout measures gaps between reads, not a total .2

For Kartly, choose an explicit failure response when current pricing is unavailable. Do not silently invent a price. A message such as “Price temporarily unavailable” lets Asha distinguish missing information from an actual offer.

The useful investigation question is not “Did the website fail?” It is “What was the last completed step?”

Try it yourself

Run this against a public example site, not Kartly’s fictional domain. In Windows PowerShell, use curl.exe instead of curl.

bash
curl --http1.1 -sS -o timing-page.html -w "dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} first=%{time_starttransfer} total=%{time_total}\n" https://example.com/

Illustrative output:

text
dns=0.040 tcp=0.100 tls=0.160 first=0.316 total=0.340

These are cumulative seconds from the start. Subtract adjacent values: TCP adds 60 ms here. The interval after TLS includes request travel, server work and response travel—not server work alone. curl downloads the response but does not render a browser page.3

Your results will differ. A configured proxy can also change what the connection timings represent.

Where this shows up

One request is now accounted for; next, Server concurrency explains what happens when many arrive together.

Common mistakes

  • Timing only the handler. Our 95 ms measurement excludes setup, travel and rendering.
  • Treating DNS as a product lookup. Its answer gets the phone to an entry point, not to a database row.
  • Treating reuse as freshness. An existing connection says nothing about whether a stored price is current.
  • Adding nested times twice. Database time is already included when the application measurement surrounds that call.

Check your understanding

Check yourselfAnswer in your head first, then reveal
Asha receives a certificate warning. Should you start by inspecting the product query?Show answerHide answer
No. TLS failed before the product request reached the handler. Inspect certificate verification and the public entry point first.
Check yourselfAnswer in your head first, then reveal
The next product request uses the existing connection but has no cached response. Does it skip PostgreSQL?Show answerHide answer
No. Connection reuse skips setup, not the handler’s data dependencies. In our design, the app still reads the product.
Check yourselfAnswer in your head first, then reveal
curl reports first=0.500 and total=0.700. Did the server spend 500 ms executing the handler?Show answerHide answer
Not necessarily. The first-byte measurement also includes lookup, connection setup and network travel. You need separate application timing to isolate the handler.
Check yourselfAnswer in your head first, then reveal
Kartly logs a successful HTML response, but Asha’s product image never appears. Can both observations be correct?Show answerHide answer
Yes. The image can require a separate request after the HTML arrives. Success for one response does not prove the entire page finished.

Summary

  1. A page appears after address lookup, connection setup, HTTP handling, response transfer and browser rendering.
  2. DNS finds an address; TCP establishes a connection; TLS protects it before HTTP carries the request.
  3. The entry point forwards to the app, which gathers product and pricing data before returning HTML.
  4. Cached answers and reused connections skip different work. Neither automatically guarantees a current price.
  5. Diagnose failures by finding the last successful boundary, and distinguish application time from the complete customer experience.

Report a mistake

Tell us what’s wrong in this lesson and we’ll fix it.