skip to content

questions

4

How does an HTTP client tell a server which responses it wants delivered first, and how did that mechanism change from HTTP/2's original dependency-tree design to RFC 9218 Extensible Priorities with its u= and i parameters?

level: middleimportance: should knowfreq 32%

answer

  1. old: PRIORITY frames, dependency tree, weights 1–256 — deprecated
  2. new: Priority header, structured fields
  3. u=0..7, lower = more urgent, default 3
  4. i = incremental → round-robin within a band
  5. PRIORITY_UPDATE frame to reprioritize mid-flight

basics

~20 s

HTTP/2 originally used a tree of stream dependencies and weights sent in PRIORITY frames. It was complex and barely implemented, so it was deprecated. RFC 9218 replaces it with a simple Priority header field: u=0..7 urgency (lower is more urgent, default 3) and i for incremental delivery.

solid answer

~60 s

Because all responses share one connection, the server must choose whose bytes to send next. HTTP/2 originally expressed that as a **dependency tree**: PRIORITY frames gave each stream a parent, a 1–256 weight, and an optional exclusive flag. It was expressive but hard to implement, clients disagreed on how to build the tree, many servers and CDNs ignored it, and RFC 9113 deprecated it. **RFC 9218 Extensible Priorities** replaces it with a signal carried in the `Priority` HTTP header field (a structured-fields dictionary), plus a `PRIORITY_UPDATE` frame for reprioritizing an in-flight request: - `u=<0..7>` — urgency; **lower is more urgent**; default 3. - `i` — incremental; true means the response is useful in pieces and may be interleaved round-robin with peers of the same urgency; false (default) means send it to completion first. The server schedules by urgency band, round-robins the incremental streams within a band, and may override the client with a `Priority` field in the response. Because it is a header field, it works identically on HTTP/2 and HTTP/3 and survives proxies.

code

http · 11 lines
http
GET /app.css HTTP/2
:authority: example.com
priority: u=1

GET /hero.jpg HTTP/2
:authority: example.com
priority: u=4, i

HTTP/2 200 OK
content-type: image/jpeg
priority: u=2, i

go deeper

for a junior

Know that the client can hint which responses matter and that the modern signal is the Priority header with u= and i.

for a middle

Explain the inverted urgency scale, the default of 3, what incremental means, and why the dependency tree was dropped.

for a senior

Describe a real scheduler (bands, round-robin within a band), and why buffering downstream or at intermediaries makes priorities ineffective.

for a principal

Argue the design lesson — expressive-but-unimplementable versus simple-and-interoperable — and where prioritization sits relative to bandwidth, buffering, and origin latency in a delivery strategy.

## Why prioritization exists at all On a multiplexed connection the server holds many ready responses and one pipe. Sending them fairly, round-robin, is often the *worst* choice for a web page: the render-blocking stylesheet finishes at the same late moment as twenty below-the-fold images, so nothing paints. Prioritization is the client's way of saying which bytes matter to the user experience. ## The original HTTP/2 scheme RFC 7540 defined a **priority tree**. Each stream could declare: - a **parent** stream it depends on (dependency: do not send me until the parent is done), - a **weight** from 1 to 256 (share of bandwidth among siblings), - an **exclusive** flag that inserted the stream between a parent and all its existing children. Signals were carried in the PRIORITY frame or in the priority fields of a HEADERS frame, and could be updated at any time. The design failed in practice: - **Implementation cost.** Servers had to maintain a mutable tree per connection, including reparenting rules for closed streams — real memory and CPU, and a denial-of-service surface (an attacker could churn the tree). - **Client divergence.** Chrome, Firefox and Safari built quite different trees, so a server tuned for one behaved oddly for another. - **Intermediaries dropped it.** Many CDNs and reverse proxies ignored priority entirely, or re-issued upstream requests without carrying the signal, so end-to-end intent was lost. - **Poor interop.** Corner cases (exclusive reprioritization, dependencies on closed streams) were widely buggy. RFC 9113 (the 2022 revision of HTTP/2) therefore **deprecated** the scheme: senders should not use it, and receivers may ignore it. HTTP/3 never adopted it. ## Extensible Priorities (RFC 9218) The replacement is deliberately small and lives at the HTTP layer, so it applies to HTTP/2 and HTTP/3 alike and can pass through caches and proxies as an ordinary field. **The `Priority` request header field** is a structured-fields dictionary with two defined parameters: - **`u`** — urgency, integer 0–7. **Lower means more urgent.** The default is 3. Browsers map resource types to bands: a document or blocking script near 0–2, ordinary CSS/JS around 2–3, images and fonts 4–5, prefetch and background work 6–7. - **`i`** — incremental, a boolean flag. `i` (or `i=?1`) means the response is usable as it arrives — progressive images, streamed HTML, event streams — so the server may interleave it round-robin with other incremental responses of the same urgency. Absent (`i=?0`, the default) means the client wants the whole thing as fast as possible, so the server should finish it before moving on within its band. The scheme is *extensible*: unknown parameters must be ignored, leaving room for future signals. **Reprioritization** uses the `PRIORITY_UPDATE` frame (a frame type defined for both HTTP/2 and HTTP/3), because a header field can only be sent once with the request. Browsers use it when, for example, an image scrolls into view or a script becomes render-blocking. **Server override.** A server may include `Priority` in the *response* to correct the client — useful when the origin knows something the client cannot, such as "this HTML is a redirect stub" or "this asset is critical for rendering". A server is also free to schedule differently for its own reasons; priorities are advisory hints, never a contract. ## How a server actually schedules A straightforward, correct implementation: 1. Group ready streams by urgency band. 2. Serve the lowest-numbered non-empty band first. 3. Within a band, send non-incremental streams sequentially in stream-ID order (roughly request order), and round-robin the incremental ones. That is a few dozen lines instead of a mutable dependency tree — the entire point of the redesign. ## Why priorities often appear not to work Even with correct signalling, scheduling only matters where the bytes queue. If the server has already handed megabytes to the kernel socket buffer, or the CDN has buffered them, reordering upstream changes nothing — the bytes are committed. Large HTTP/2 flow-control windows have the same effect. Intermediaries that re-originate requests may drop the `Priority` field. And when the connection is not the bottleneck (small page, fast link, backend-latency-bound), prioritization is invisible by definition. Diagnose in that order: is the signal being sent, does it survive to the scheduler, and is the connection actually saturated?

  • Is a higher u value more urgent?
    No — urgency is inverted: u=0 is the most urgent and u=7 the least, with 3 as the default. It is the single most common mistake with the header, and it silently deprioritizes exactly the resources you meant to promote.
  • What does marking a response incremental actually change?
    With i set, the server may interleave that response round-robin with other incremental responses at the same urgency, so several progressive resources render partially in parallel. Without it, the server should finish one response before starting the next in that band, which is what you want for a stylesheet or a script that is useless until fully downloaded.

The dependency tree was a full org chart of who waits for whom; Extensible Priorities is a triage tag with a number and a flag for "deliver in slices".

saying these in an interview costs you the question

  • Reading u= as "higher is more important", which reverses the intended order
  • Claiming HTTP/2's PRIORITY frames and dependency tree are the current mechanism
  • Treating priorities as a guarantee rather than a hint the server may ignore or override
  • Expecting prioritization to help when the connection is not the bottleneck or the bytes are already buffered downstream

context

open as a page

HTTP/2 server push let a server send a response the client never asked for, announced with a PUSH_PROMISE frame. Explain how it worked and why browsers such as Chrome ended up removing support for it.

level: middleimportance: should knowfreq 36%

basics

~20 s

The server sent PUSH_PROMISE reserving an even stream ID, then delivered a response the client had not requested. Servers could not know what the client already had cached, so most pushes were wasted bytes competing with the HTML for bandwidth. Chrome removed it in 2022.

open as a page

You want to use HTTP status 103 Early Hints in production to speed up page loads. Describe how it travels through the request path, what must be true for it to help, and what can break.

level: seniorimportance: should knowfreq 26%

basics

~20 s

The origin sends a 103 informational response with Link: rel=preload or rel=preconnect fields, then later the real response. It only helps when origin think-time is large enough to cover a fetch, every hop forwards 1xx responses, and the hints match what the page actually needs. Wrong hints waste bandwidth.

open as a page

With HTTP/2 server push removed from browsers, how would you decide between HTTP 103 Early Hints, Link: rel=preload on the final response, inlining critical CSS, and doing nothing, for delivering render-blocking resources on a high-traffic site?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Decide by where the time actually goes. Long origin think-time favours 103 Early Hints; a fast origin makes preload on the final response the cheap default; a tiny, stable critical set justifies inlining despite losing cacheability; otherwise do nothing and fix caching or the number of critical resources instead.

open as a page