Backend Interview: How Would You Design HTTP Priority Scheduling?
Prompt and applicability
A page requests HTML, critical CSS, fonts, images, and background data at the same time. Mobile bandwidth is limited. The team wants critical resources to finish earlier with HTTP Priority, but worries about proxy overrides, starvation, cache hits bypassing application scheduling, and packet-loss retransmissions changing the order. Design server and proxy scheduling across HTTP/1.1, HTTP/2, and HTTP/3, including validation.
RFC 9218 defines a version-independent Priority header and reprioritization frames for HTTP/2 and HTTP/3. It communicates preferences, not a delivery SLA. A strong answer separates signals, schedulers, caches, and transport, and states what each layer may reject or rewrite.
What the interviewer is testing
- Knowing that urgency ranges from 0 to 7, with smaller values more urgent and a default request value of 3.
- Explaining how incremental changes concurrency and completion time for resources with the same urgency.
- Distinguishing HTTP/1.1 request ordering from multiplexed scheduling in HTTP/2 and HTTP/3.
- Treating Priority as a hint rather than an SLA and handling hostile or incorrect client declarations.
- Considering cache hits, proxy reprioritization, QUIC retransmission, fairness, and cross-connection competition.
- Designing a reversible rollout with user-experience and fairness metrics.
Clarifications to ask first
- Is the goal lower LCP, lower tail latency for one API, or more background throughput? Each changes the policy and metrics.
- Are resources on one connection, CDN node, and cache key? A single-connection priority cannot be compared directly across connections.
- Which resources can be processed incrementally? Complete CSS, fonts, and non-chunk-consumable compressed objects should not be split merely because urgency is high.
- Do proxies forward request and response headers and PRIORITY_UPDATE? Does every hop use HTTP/2 or HTTP/3?
- Are there tenant, user, or security boundaries that prevent low-value requests from claiming the top priority?
A 30-second answer
I would define the target as user-visible completion time and classify resources with defaults. The client Priority header is an input; the server recalculates it using resource type, cache state, connection, and fairness. Urgency from 0 to 7 gives relative order, while incremental is for responses that can be consumed as bytes arrive. HTTP/1.1 relies on connection request order; HTTP/2 and HTTP/3 use a scheduler and, when supported, reprioritization frames. Cache hits, retransmission, and cross-connection bandwidth need separate treatment. In a canary I would track LCP, critical-resource completion, background tails, starvation, and bandwidth, and disable rewriting if the guardrails fail.
Step-by-step deep dive
1. Turn product goals into scheduling goals
HTML, render-blocking CSS, and critical fonts should finish early. Large images can be less urgent; analytics and prefetch should not compete with the critical path. Background APIs still need a maximum wait. Give each class a wait bound and bandwidth share instead of saying only that critical requests go first.
2. Explain the two Priority parameters
u is urgency: 0 is highest and 7 lowest, with a default request value of 3. i says that a response can be processed incrementally. With i, a server may split bandwidth among same-urgency resources so they all start sooner, while each can finish later. Without it, sequential delivery is usually better for objects that cannot be consumed in pieces.
GET /style.css HTTP/1.1
Host: example.test
Priority: u=1
GET /hero.jpg HTTP/1.1
Host: example.test
Priority: u=4, iA server response can also send a priority hint to a downstream intermediary. Omitting the response header means the server did not change the client priority. Unknown parameters should be ignored; a client value is not an authorization or billing credential.
3. Choose boundaries by protocol version
HTTP/1.1 has no built-in multiplexed priority; server behavior mainly follows connection order, connection concurrency, and queues. HTTP/2 and HTTP/3 share bandwidth on one multiplexed connection, so a scheduler can select the next data segment using urgency and incremental. Both can change the preference of an existing request with a PRIORITY_UPDATE mechanism, but implementations do not promise immediate or strict execution.
4. Handle caches and proxies
A cache hit may be returned without reaching origin application scheduling. Keep priority effects in transmission and cache-fill stages; a normal Priority header must not change object authorization or silently become a cache key. A proxy can coalesce client connections, use different backend connections, or rewrite response priority. Record the original value, final value, and reason at each boundary. Cache behavior remains governed by fields such as Cache-Control and Vary.
5. Handle retransmission and fairness
QUIC and TCP retransmit lost data. A high-urgency new object should not automatically outrank low-urgency retransmission, because retransmission may unblock a response already in progress. RFC 9218 leaves this trade-off to transport and application policy. Across connections, u=0 on one connection cannot guarantee the highest global bandwidth. Add per-connection quotas, aging, and a maximum consecutive send budget so background work cannot starve forever.
6. Prevent priority abuse and propagation errors
A client can label every request u=0, so the server should correct it using resource allowlists, authenticated identity, page phase, and history. Cap maximum urgency, apply tenant quotas, fall back to defaults for unknown resources, and record overrides. Across proxies, preserve meaning rather than blindly copying strings. A hop that does not understand the signal should safely ignore it without changing response correctness.
7. Canary, fallback, and acceptance
Start with a fixed page and low-risk CDN nodes, enabling server rewriting for a fraction of connections. Compare the same network, device, cache state, and resource set. Measure LCP, critical CSS completion, font-blocking time, background p95 and p99, time to first byte, duplicate downloads, connection utilization, and maximum low-priority wait. Improvements must be weighed against errors, bandwidth cost, and cross-tenant fairness. Restore default priorities immediately when scheduler behavior is abnormal.
High-quality sample answer
I would define first-paint completion and background-request goals, then classify HTML, render-blocking CSS, fonts, incremental media, and background data. The client Priority header is only a hint. I would correct it using a resource allowlist, page phase, cache state, and tenant quota: use urgency 3 as the default, lower the value for critical resources, raise it for background work, and use incremental only when the consumer can process partial data.
HTTP/1.1 relies mostly on connection and queue order. HTTP/2 and HTTP/3 use a multiplexed scheduler and optional reprioritization frames. Cache hits must not change authorization or cache keys, and proxies should record original and final priorities. Retransmission cannot automatically lose to new data, so I would add connection quotas, aging, and a consecutive-send cap. A canary compares LCP, critical completion, background tails, bandwidth, and low-priority wait; worsening guardrails disable server rewriting.
Common mistakes
- Treating
Priority: u=0as a completion SLA → congestion, cache state, and retransmission still matter → use it as scheduler input with measurable goals. - Treating
incrementalas “finishes faster” → sharing bandwidth can delay each object's completion → use it only when partial consumption has value. - Letting client priority change cache keys or authorization → cache fragmentation or privilege mistakes follow → keep cache and permission contracts independent.
- Analyzing one HTTP/2 connection only → cross-connection competition can reverse results → slice validation by connection, node, network, and tenant.
- Letting high-priority retransmission always beat other data → new responses or background streams can starve → include retransmission policy, quotas, and aging.
- Looking only at LCP → background tails and bandwidth cost may worsen → set user, reliability, fairness, and cost guardrails together.
Follow-ups and responses
What if every client sends u=0?
Treat it as an untrusted hint. Recalculate using resource type, page phase, authenticated identity, and tenant quota; return abnormal or unknown requests to the default and record why they were overridden.
Does incremental always improve experience?
No. It shares bandwidth among same-urgency responses, making several start earlier while each can finish later. Use it when the consumer can process partial bytes and that start-time benefit matters.
Is Priority needed for a cache hit?
A hit usually bypasses origin application queues, but a proxy may still schedule transmission on a connection. Priority must not enter authorization or arbitrarily change the cache key; observe hit, fill, and send stages separately.
Should retransmission or high-urgency new data go first after loss?
There is no context-free answer. Retransmission may unblock an in-progress response, while new data may matter more to the user. Combine stream dependency, incremental capability, congestion state, and a measurable experience objective.
Can HTTP/1.1 implement the same priority?
It lacks the multiplexed priority tree of HTTP/2. Server queues, connection concurrency, and resource ordering can approximate a policy, but it remains an implementation strategy that must be tested for head-of-line blocking and connection competition.
How do you prove that low-priority work is not starved?
Record queue and first-send time for every request, then calculate maximum wait and p99 by resource, connection, and tenant. Under sustained high-urgency load, verify that aging, quotas, or deadlines still let low-priority work receive service.