A mock interview begins with a small incident: a gateway returns an error, yet an application behind it records an unexpected request. The client used HTTPS. Both services passed their unit tests. Your task is to explain what each hop believed it received, then design a boundary that remains safe when a request is incomplete, malformed, or translated between HTTP versions.
This is a useful senior and staff engineer practice problem because it connects protocol details to architecture ownership. You must reason about parsers, streaming, connection reuse, compatibility, and rollout evidence together. The exercise below is an original practice scenario, not a claim about any employer's interview questions. Its central invariant is simple: each accepted logical request must have one unambiguous interpretation across the forwarding chain.
1. Draw the protocol on every connection
Start with a concrete path: client, edge gateway, reverse proxy, application. Label each connection with its HTTP version, where TLS terminates, whether connections are pooled, and which component validates the message. Avoid a single arrow marked “HTTPS” spanning the entire diagram. In your proposed design, require an explicit configuration record for each hop before claiming that a browser-facing protocol determines backend behavior.
Request smuggling concerns disagreement about message boundaries between systems in a chain. James Kettle's original HTTP Desync Attacks research demonstrates how differing interpretations can leave bytes on a backend connection that affect a subsequent request. Use that historical research to explain the failure class; it is not evidence that a particular current service is vulnerable.
For the mock incident, ask where the unexpected request identifier first appeared. Compare edge admission records with backend request records, and identify whether the same upstream connection carried both requests. Keep payloads synthetic and exclude credentials from diagnostic logs. Your first deliverable is a hypothesis with an observable discriminator: a framing disagreement should produce a different sequence of interpreted requests at two adjacent hops.
2. Separate framing from application meaning
HTTP/1.1 framing determines where a message ends. Under RFC 9112, section 6.3, Transfer-Encoding takes precedence over Content-Length when both appear, but this combination ought to be treated as an error. An intermediary that forwards it must remove the received Content-Length and process Transfer-Encoding first. A server receiving both fields must close the connection after responding. Invalid conflicting lengths are not an invitation to choose whichever value seems convenient.
For this practice design, propose rejecting ambiguous framing at the first controlled ingress and terminating the affected HTTP/1.1 connection. Distinguish this deliberately strict policy from a claim that the specification requires rejection of every duplicate length: RFC 9112 permits processing valid identical values as one length. Ask whether legitimate clients depend on that allowance before tightening production behavior.
Now explain the application boundary. An authorization failure or an unsupported operation is a decision about a request; it does not by itself establish where that request's bytes end. Your diagram should therefore place protocol validation before application routing, with explicit error handling between them. Resist implementing a second ad hoc framing parser in middleware. Choose maintained protocol implementations and test their composition.
Account for the bytes after an early response
RFC 9112, section 9.3 requires a server to read the entire request body or close the connection after responding. Otherwise, leftover bytes can be interpreted as another request. In your design, bounded draining may suit a valid rejected request; uncertain framing should make the connection ineligible for reuse.
Describe that lifecycle precisely in the interview: detect, classify, respond if appropriate, cancel downstream work where possible, and retire the affected connection. Assign responsibility for each transition. “Return 400” is incomplete if the connection pool can immediately hand the same uncertain byte stream to another operation.
3. Treat protocol translation as a validation boundary
HTTP/2 provides streams and frames, but it still has malformed-message rules. RFC 9113, section 8.1.1 says that, for messages with content, Content-Length must agree with the relevant DATA payload lengths. Intermediaries must not forward malformed messages once detected. Connection-specific fields are prohibited, with the limited TE: trailers exception described in section 8.2.2.
A useful follow-up is: “Our edge accepts HTTP/2 and forwards HTTP/1.1. What must be true before reuse?” Propose that the translator validate incoming fields, construct outgoing framing from its parsed message model, and manage the downstream connection according to the outgoing protocol. Do not treat binary framing on the client connection as proof of safe serialization on another connection.
There is a timing wrinkle. RFC 9113 acknowledges that progressive processing can occur before a malformed message is identified. If you stream to an application, your design needs an answer for partial delivery and side effects. Explain whether the operation stages data until completion, supports an explicit abort, or cannot safely begin until the message is validated. This is a product-specific choice, not a universal recommendation to buffer everything.
RFC 9114, section 4.1.2 imposes analogous malformed-message checks for HTTP/3, including content-length consistency, and uses H3_MESSAGE_ERROR for detected malformed messages. The practice lesson is to specify rejection scope for each protocol. A stream error and closing an entire shared connection have different consequences for other work; an answer should identify which condition requires which action.
4. Give forwarded fields explicit ownership
Framing is only part of the contract. RFC 9110, section 7.6.1 requires an intermediary to parse Connection, remove fields it names as connection-specific, and remove or replace Connection itself before forwarding. A fixed list of familiar header names does not express that full rule.
For the proposed service, also define who creates trusted routing and identity metadata. Specify which information comes from the authenticated connection, which comes from validated client input, and which the edge overwrites. Ask how an application distinguishes metadata generated by a trusted gateway from a similarly named client field. Treat this as a separate trust-design question rather than assuming a syntactically valid header is authoritative.
Choose a concrete runtime example without turning its defaults into universal HTTP rules. Node.js documents insecureHTTPParser as enabling parser leniency and advises avoiding it. During a mock review, request the deployed runtime version and actual parser options. A compatibility flag deserves a named owner, a reason, and a retirement test; the word “default” in current documentation does not prove a running deployment uses it.
5. Make streaming and buffering costs visible
NGINX documents request buffering as reading the complete body before sending it upstream when enabled. With buffering disabled, it forwards the body as received; once body transmission has started, the request cannot be passed to the next server. That behavior gives you a concrete operational tradeoff to discuss, but buffering alone is not a complete defense against parser disagreement.
Use an explicitly illustrative capacity calculation: 400 simultaneous requests with 2 MiB bodies would represent 800 MiB of body storage if each were fully resident at once. This excludes metadata, copies, allocator overhead, and every other workload. It is a deliberately simplified upper-bound scenario for those bodies, not a measured NGINX memory estimate; real buffering can involve disk and different residency patterns.
Then define the controls your proposed system would need: body-size limits, admission limits, storage budgets, and deadlines for receiving headers and bodies. Specify whether a timeout is total elapsed time or inactivity between reads. For a large upload, staging content and committing only after validation may be appropriate. For a small command, full validation before execution may be simpler. Explain the choice using the operation's side effects and resource budget.
6. Roll out a policy with compatibility evidence
A managed load balancer setting is a policy choice, not a substitute for understanding the chain. AWS documents ALB desync mitigation modes with different treatment of request classifications. Defensive mode allows requests classified as ambiguous but closes client and target connections; strictest mode blocks those requests. The documentation also identifies increased new-connection usage as a possible cost of defensive handling.
In the mock plan, inventory legitimate client versions and examine classification counts before changing policy. Build a small compatibility corpus from sanitized examples. Set a rollout gate for unexpected rejection rates and another for unsafe acceptance. A rollback should preserve a safe containment policy while the team fixes affected clients; “accept everything again” should not be the only recovery option.
Measure parser rejections by reason, protocol conversion path, connection retirement, and application-visible request counts. Include latency and connection churn to expose operational regressions. Avoid logging complete request bodies to investigate a header problem. State who owns the edge, proxy configuration, and application behavior, because a safe policy can decay when one component upgrades independently.
7. Rehearse with a boundary-focused rubric
The following is an illustrative practice rubric, not an employer scorecard. Use an isolated test environment that you control, synthetic users, and a harmless recording backend. Have a practice partner vary one condition at a time: incomplete body, conflicting framing metadata, early application rejection, protocol translation, or client disconnect. Do not run desynchronization experiments against shared production traffic.
For each case, write the expected outcome before sending it. Record whether the backend received a request, whether partial content was exposed, and whether the relevant stream or connection remained reusable. Follow an invalid request with a valid synthetic request to check isolation. A single status code is insufficient evidence; inspect the interpretation and lifecycle on both sides of the boundary.
Evaluate your explanation in four dimensions: a clearly stated invariant, correct protocol distinctions, a bounded resource policy, and an observable rollout gate. If you cannot explain when side effects become possible, revisit streaming. If you cannot explain what happens to unread bytes, revisit connection reuse. If you cannot name an owner for conflicting parser settings, revisit the operational design.
Finish the rehearsal with a short decision record: the accepted request contract, the rejection policy, the connection cleanup rule, the compatibility exceptions, and the tests needed before launch. This produces a reusable interview artifact. It also keeps the discussion grounded in evidence: a safe boundary is something your design can demonstrate under failure, not merely a box labeled gateway.
Practice explaining your HTTP design
Use Interview Copilot to prepare technical explanations, rehearse follow-up questions, and refine the tradeoffs in your next mock interview.
Start practicingSources & References
- RFC 9112: HTTP/1.1 framing and connection persistence
- RFC 9110: HTTP semantics and intermediary fields
- RFC 9113: HTTP/2 malformed messages and field validity
- RFC 9114: HTTP/3 malformed requests and responses
- Node.js: HTTP parser options and server APIs
- NGINX: Proxy request buffering and forwarding
- AWS: Application Load Balancer desync mitigation modes
- James Kettle, PortSwigger (2019): HTTP Desync Attacks