# HTTP Request Lifecycle: From URL to Response (/docs/web-platform/http-request-lifecycle)



# HTTP Request Lifecycle: From URL to Response [#http-request-lifecycle-from-url-to-response]

## TL;DR [#tldr]

On a major launch day, an e-commerce checkout page experiences inexplicable 1.2-second latency spikes for first-time shoppers, while recurring users finish checkout in under 80ms. The team suspects slow database queries, but distributed APM traces reveal database time is under 15ms. The real bottleneck? An unpooled cold-request cascade: full recursive DNS lookup, TCP 3-way handshake, TLS 1.3 negotiation, and edge cache misses occurring on every unpooled API fetch.

> 💡 &#x2A;*Rule of thumb:** &#x2A;*An HTTP request is a semantic exchange, not an automatic network connection.** A single request may complete in 0ms inside local memory (cache hit), traverse an existing persistent socket in 1-RTT, or incur multiple round trips across DNS, TCP, and TLS if connections are unpooled.

* **Semantic contract vs. transport pipe:** HTTP standardizes request/response semantics (RFC 9110); transport layers (TCP, TLS, QUIC/UDP) provide the underlying byte stream.
* **Four request path archetypes:** Paths branch early: fresh client cache hit (zero network), warm connection reuse (zero handshake), intermediary CDN hit (zero origin compute), or full cold setup.
* **Protocol multiplexing:** HTTP/1.1 introduced keep-alive reuse; HTTP/2 added binary multiplexing over single TCP streams; HTTP/3 eliminated TCP head-of-line blocking using QUIC over UDP.
* **Cache revalidation:** Intermediary CDNs and local caches evaluate `Cache-Control`, `ETag`, and `304 Not Modified` headers before request traffic ever touches origin application code.
* **Fatal pitfall:** Treating every `fetch()` call as a self-contained operation without connection reuse or keep-alive agents—forcing the client or server runtime to tear down and recreate TCP/TLS handshakes on every outgoing network hop.

## Start with four common request paths [#start-with-four-common-request-paths]

<AtlasIllustration id="http-request-paths" />

Before memorizing protocol layers, ask which of these shapes resembles the request you are debugging.

### 1. Fresh client-cache hit [#1-fresh-client-cache-hit]

```text
construct request
      ↓
client cache finds a fresh response
      ↓
return stored response
```

No network request needs to leave the client.

### 2. Cache miss, existing connection [#2-cache-miss-existing-connection]

```text
construct request
      ↓
cache miss
      ↓
reuse suitable connection
      ↓
send HTTP request
      ↓
response
```

The HTTP request is new even though the connection is not.

### 3. No suitable connection [#3-no-suitable-connection]

The client may need address resolution, transport/connection establishment, and security setup before it can carry the HTTP exchange.

### 4. Intermediary cache hit [#4-intermediary-cache-hit]

```text
client cache miss
      ↓
network request
      ↓
shared/intermediary cache hit
      ↓
response

origin application processing: skipped
```

These paths are why there is no single mandatory “DNS → TCP → TLS → app” sequence for every HTTP request.

## Keep HTTP semantics separate from network setup [#keep-http-semantics-separate-from-network-setup]

```text
HTTP semantics
request method + target + fields + optional body
                      ↓
response status + fields + optional body
```

Around that semantic exchange, a client may need caches, connection reuse or setup, intermediaries, and origin processing. DNS, transport, and TLS can affect latency, but they are not the semantics of `GET`, `404`, or `Cache-Control`.

<TermBox term="Origin">
  An **origin** is the authority identified by a URL's scheme, host, and port.

  **Why it matters here:** traffic for one origin can still pass through CDNs, reverse proxies, gateways, or load balancers before application code runs. “The request targeted this origin” does not prove which component generated the response.
</TermBox>

<TermBox term="Intermediary">
  An **intermediary** is a participant between a client and an origin, such as a proxy, gateway, or shared cache.

  **Why it matters here:** an intermediary can forward traffic, route it, apply permitted transformations, or sometimes answer from cache without the origin application processing the request.
</TermBox>

## Cache before network [#cache-before-network]

<TermBox term="Fresh / stale cache entry">
  A cached response is **fresh** when caching rules allow it to be reused without first validating it with another server. A stored response is **stale** when it is no longer fresh.

  **Why it matters here:** a fresh response may eliminate the network exchange entirely, while a stale response may trigger validation or retrieval of a new representation.
</TermBox>

<TermBox term="Revalidation">
  **Revalidation** asks whether a stored response may still be reused. A common path sends a conditional request and receives `304 Not Modified`, but validation can also produce a new representation or another result.

  **Why it matters here:** a `304` means a network HTTP exchange occurred, while the response representation ultimately used by the client can still come from previously stored data.
</TermBox>

<AtlasIllustration id="http-cache-revalidation" />

For the Fetch Standard's default cache mode, Fetch broadly does this on the way to a network fetch:

* a matching fresh stored response can be returned without network validation;
* a stale stored response may lead to a conditional network request;
* a cache miss proceeds toward a normal network fetch.

<TermBox term="Cache key">
  A **cache key** is the information a cache uses to decide which stored response can match a request. It usually starts from the target URI and can vary further based on cache rules such as `Vary` and deployment-specific behavior.

  **Why it matters here:** two requests that look similar to a developer can map to different cached representations when fields that participate in the key differ.
</TermBox>

HTTP caching has more rules than this summary, including authorization interactions, `Vary`, freshness calculation, and directives. The practical debugging rule is: &#x2A;*verify whether the request reached the network before blaming DNS, TLS, a CDN, or backend code.**

## Connection reuse and setup [#connection-reuse-and-setup]

<TermBox term="Connection reuse">
  **Connection reuse** means carrying a new HTTP exchange over a suitable connection that already exists instead of establishing a brand-new one.

  **Why it matters here:** HTTP request latency and connection-establishment latency are different measurements. Reused HTTP/2 or HTTP/3 connections can carry many requests without repeating full setup for each request.
</TermBox>

When a request must use the network, first ask whether a suitable connection already exists. If one can be reused, that exchange does not need a brand-new transport connection or brand-new secure session.

<TermBox term="Transport">
  A **transport** is the networking mechanism that carries data between endpoints. HTTP/1.1 and HTTP/2 commonly use TCP; HTTP/3 uses QUIC.

  **Why it matters here:** HTTP semantics are defined above the transport. Changing the transport can change connection behavior and performance without changing what methods, status codes, and fields mean.
</TermBox>

<TermBox term="TLS secure session">
  **TLS** provides authenticated, encrypted communication for HTTPS. The exact setup relationship depends on the transport: HTTP/1.1 and HTTP/2 commonly use TLS over TCP, while QUIC incorporates TLS into QUIC connection establishment.

  **Why it matters here:** a diagram that draws “transport” and “secure session” as separate conceptual stages must not be mistaken for one identical handshake sequence across every HTTP version.
</TermBox>

If no suitable connection exists, setup can include obtaining a usable network address, establishing the required transport, establishing security state for HTTPS, and then carrying the HTTP request. Address caches, connection pools, protocol negotiation, and network environment can skip or alter parts of that work.

## HTTP/1.1, HTTP/2, and HTTP/3 [#http11-http2-and-http3]

<AtlasIllustration id="http-version-architecture" />

RFC 9110 defines semantics shared by the major HTTP versions. The versions differ in message carriage and connection behavior.

### HTTP/1.1 [#http11]

HTTP/1.1 expresses HTTP messages using its message syntax over a connection. Connections can be persistent and reused for multiple requests.

### HTTP/2 [#http2]

<TermBox term="Multiplexing">
  Multiplexing allows multiple independent exchanges to make progress over one connection using separate logical streams instead of requiring one connection per exchange.

  **Why it matters here:** multiple HTTP/2 requests can share one connection, so a connection setup cost should not be attributed separately to every stream carried over it.
</TermBox>

HTTP/2 preserves HTTP semantics while introducing binary framing, field compression, and multiple concurrent exchanges on one connection.

### HTTP/3 [#http3]

<TermBox term="QUIC">
  QUIC is a secure transport protocol built over UDP that provides features such as multiplexed streams and integrates TLS into connection establishment. HTTP/3 maps HTTP semantics over QUIC.

  **Why it matters here:** HTTP/3 does not use the same TCP-then-TLS layering commonly drawn for HTTPS over HTTP/1.1 or HTTP/2.
</TermBox>

The practical debugging rule is:

> Identify the HTTP version and connection state before inferring which setup work belongs to this request.

## Constructing the request [#constructing-the-request]

Before an HTTP exchange can happen, the caller needs request semantics: a method, target, fields, and optionally a body.

For a browser Fetch operation, the browser also applies Fetch-specific policy such as request mode, credentials mode, redirect mode, and cache mode. Those policies are part of the browser's fetching model; they are not themselves generic HTTP semantics.

A browser can therefore prevent, redirect, or satisfy a fetch before application code on the origin sees the request.

## Intermediaries and origin processing [#intermediaries-and-origin-processing]

<AtlasIllustration id="request-boundary-ownership" />

A request that leaves the client can still encounter several participants before business logic runs:

* forward proxy;
* CDN or shared cache;
* reverse proxy;
* API gateway;
* load balancer;
* service ingress;
* origin application.

This is illustrative, not a required topology. When diagnosing a response, ask **which participant generated it**. Response fields, trace context, server timing, logs, CDN headers, gateway logs, or application correlation IDs can help establish the boundary.

A fast `404` from an edge cache and a `404` generated by application routing have the same HTTP status semantics but very different debugging paths.

## Response processing [#response-processing]

Receiving bytes successfully over the network does not mean the application operation succeeded. Separate:

1. **network/connection failure** — no usable HTTP response was obtained;
2. **HTTP error response** — for example `404`, `429`, or `503`;
3. **application-level failure represented inside a nominally successful HTTP response** — for example a domain error encoded in a `200` response body.

Once a response exists, a client may stream or buffer its body, update caches, decode content, apply redirect behavior, or expose the result to application code.

A redirect response is still an HTTP response. Following it creates another request whose cache and connection path must be evaluated for the new target.

## Try it: Request Path Explorer [#try-it-request-path-explorer]

The explorer compares predefined request paths. It does not perform live network requests and does not invent representative millisecond timings.

<HttpRequestPathExplorer />

Use it to answer:

> Which stages occurred for this request, which stages were skipped, and which stages depend on runtime or deployment state?

## How to debug a slow or surprising request [#how-to-debug-a-slow-or-surprising-request]

### Production scenario: The "phantom 200" and missing server logs [#production-scenario-the-phantom-200-and-missing-server-logs]

An engineering team receives customer bug reports: after updating user profile details, the web page continues displaying stale information. In DevTools Network panel, the engineer observes:

* Status: `200 OK`
* Response Payload: Stale user profile data
* Timing: `3ms`

The engineer suspects backend caching or database bugs, spending over two hours searching application access logs, only to discover **zero incoming requests recorded for that user**.

* **Impact:** Significant engineering time wasted across frontend and backend teams due to misdiagnosing the failure boundary.
* **Root cause:** The engineer missed the DevTools Size column: it read `(from disk cache)`! The request never left the local machine. A preceding response included `Cache-Control: public, max-age=3600`, instructing the browser to fulfill subsequent fetches entirely from local storage.
* **Correct pattern:**
  1. Inspect the Size/Transferred column first: `(from disk cache)` or `(from memory cache)` confirms client-side cache fulfillment.
  2. Emit `Cache-Control: no-cache` or `private, no-store` on sensitive, user-specific API endpoints to ensure the browser performs a network exchange.

### 1. Did a network request occur? [#1-did-a-network-request-occur]

First determine whether a client cache or other local mechanism satisfied the request. If the request never reached the network, DNS, connection establishment, CDN routing, and origin latency are not the cause of this exchange.

### 2. Was there a redirect or revalidation? [#2-was-there-a-redirect-or-revalidation]

A single user action can cause more than one HTTP exchange. Redirects and conditional validation requests add exchanges that can look like duplicated work unless you inspect statuses and request fields.

### 3. Was a connection reused? [#3-was-a-connection-reused]

Check the negotiated protocol and connection information available in your tooling. If the request reused an HTTP/2 or HTTP/3 connection, do not automatically attribute connection-establishment cost to every request carried on that connection.

### 4. Which participant returned the response? [#4-which-participant-returned-the-response]

Distinguish client cache, shared cache/CDN, gateway, reverse proxy, and origin application where your deployment provides evidence for those layers.

### 5. Where did time accumulate? [#5-where-did-time-accumulate]

Separate, where tooling allows:

* waiting for a connection path;
* connection/security setup;
* request upload;
* intermediary/origin processing before response headers;
* response body transfer;
* client-side processing after bytes arrive.

Tool timing names and precision vary. Treat them as observations from that tool, not universal HTTP fields.

### 6. What kind of failure occurred? [#6-what-kind-of-failure-occurred]

A DNS failure, TLS/QUIC connection failure, HTTP `503`, and domain validation error are different failure boundaries with different owners and retry decisions.

## Failure boundaries [#failure-boundaries]

| Boundary                  | Example symptom                                 | Next evidence to inspect                          |
| ------------------------- | ----------------------------------------------- | ------------------------------------------------- |
| Name/address resolution   | host cannot be resolved                         | resolver/network diagnostics                      |
| Connection/security setup | connection or certificate/security failure      | connection details, TLS/QUIC diagnostics          |
| HTTP intermediary         | gateway/CDN/proxy-generated status              | intermediary logs and response fields             |
| Origin application        | application status/error                        | application logs, traces, correlation ID          |
| Response body transfer    | truncated/aborted stream                        | network logs, server/client cancellation evidence |
| Client processing         | parse/policy/application failure after response | browser/app logs and Fetch policy context         |

Do not infer a later boundary when an earlier one already failed. An HTTP `500`, for example, proves an HTTP response existed; it is not a DNS failure.

## Retries and request semantics [#retries-and-request-semantics]

Retry decisions belong to the operation's semantics, not just to whether a transport attempt failed.

HTTP defines method properties such as safety and idempotency, but an application's real side effects still depend on endpoint design. Before retrying a state-changing operation, determine whether it is safe to repeat, whether an idempotency contract exists, whether the previous attempt might have succeeded despite response loss, and which failure classes the retry policy covers.

## Exercise [#exercise]

A browser shows an API request with these observations:

* the browser reports HTTP/2;
* an existing connection identifier is reused;
* the request receives `304 Not Modified`;
* the application service has no normal request-processing log for the resource body;
* the browser displays the previously stored representation.

Answer:

1. Which facts prove that a network HTTP exchange occurred?
2. Which connection-setup stages can you avoid attributing to this request?
3. Why does `304 Not Modified` not mean “the browser received a second full copy of the body”?
4. What evidence would you need before claiming the request reached the application service rather than being handled by an intermediary validator?

<details>
  <summary>
    Show the reasoning
  </summary>

  1. **Proof of network HTTP exchange:**\
     The `304 Not Modified` status code is only produced when a conditional request (e.g., carrying `If-None-Match` or `If-Modified-Since`) traverses the network and receives a server/intermediary response. If the request was satisfied entirely by the local browser cache without network involvement, DevTools would show `200 OK (from disk cache)`.
  2. **Avoided connection-setup stages:**\
     Because the browser reports that an existing HTTP/2 connection identifier was reused, this exchange incurred zero latency for DNS resolution, TCP three-way handshake, and TLS session negotiation.
  3. **Why 304 does not transfer the full body:**\
     A `304 Not Modified` response conveys that the client's cached representation remains fresh. Its response body is empty (zero payload bytes). The browser satisfies rendering by reading the full body from its local cache storage.
  4. **Distinguishing application service from intermediary:**\
     You would need end-to-end evidence such as an application trace ID or matching timestamped access log from the application service container. If an edge CDN or reverse proxy stored the ETag validation mapping, that intermediary can return `304` directly without forwarding the request to the application origin.
</details>

## Agent rule [#agent-rule]

> When debugging or changing code around an HTTP request, do not assume a universal `DNS -> connect -> TLS -> origin` sequence. Determine whether a cache satisfied the request, whether a suitable connection was reused, which HTTP version carried the exchange, which intermediary or origin generated the response, and whether the observed failure is network-level, HTTP-level, or application-level. Treat DNS, transport, and secure-session setup as adjacent conditional stages, not HTTP semantics themselves.

When debugging or changing code around an HTTP request, verify against this checklist:

* [ ] **Client cache verification:** Did the request reach the physical network, or was it answered by `disk cache` / `memory cache`?
* [ ] **Connection reuse check:** Did this request reuse an existing socket/TLS session or require fresh DNS, TCP, and TLS handshakes?
* [ ] **Protocol version awareness:** Is the exchange running over HTTP/1.1 (sequential pipeline), HTTP/2 (multiplexed TCP streams), or HTTP/3 (QUIC UDP streams)?
* [ ] **Intermediary vs Origin boundary:** Does the response status and headers come from an Edge CDN, API Gateway, WAF, or the origin backend?
* [ ] **Revalidation and freshness:** Are `ETag`, `Last-Modified`, and `Cache-Control` configured so stale entries can be verified with lightweight `304 Not Modified`?
* [ ] **Failure tier isolation:** Is an observed issue a network/connection disconnect, an HTTP protocol error (e.g., 502/504), or a domain business logic error in a 200 body?
* [ ] **Safe retry semantics:** Are automated retries restricted strictly to idempotent requests or protected by idempotency keys?

## Related concepts [#related-concepts]

* **DNS Resolution** — how names become usable network addresses.
* **TLS and HTTPS** — authentication, confidentiality, and secure-session establishment.
* **HTTP Caching** — freshness, validation, cache keys, directives, and shared/private caches.
* **CDN Behavior** — edge routing, shared caching, revalidation, and origin shielding.
* **Backend Request Lifecycle** — what happens after a request reaches service-side application boundaries.
* **Idempotency** — how to make repeated operations safe when retries or ambiguous outcomes are possible.

## Sources [#sources]

Primary references verified on 2026-09-09:

* [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
* [RFC 9111 — HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111.html)
* [RFC 9113 — HTTP/2](https://www.rfc-editor.org/rfc/rfc9113.html)
* [RFC 9114 — HTTP/3](https://www.rfc-editor.org/rfc/rfc9114.html)
* [RFC 9001 — Using TLS to Secure QUIC](https://www.rfc-editor.org/rfc/rfc9001.html)
* [WHATWG Fetch Living Standard](https://fetch.spec.whatwg.org/)
