skip to content

Across an edge proxy, the HTTP server and the framework, where would you own request-size and header limits for a fleet of services, and why?

level: principalimportance: should knowfreq 33%

answer

  1. every parsing layer caps for its own reason
  2. edge strictest, server backstop, framework per route
  3. disagreement, not strictness, causes incidents
  4. rejections below the seam are invisible to the app
  5. exceptions are paired, documented changes

basics

~10 s

Cap deliberately at every layer: the edge strictest as the platform default, the server looser as a process-protection backstop, the framework tightest per route. The failure to avoid is caps that differ by accident.

solid answer

~50 s

Every layer that parses a request must cap it, so the question is not *which one* but *how the numbers relate*. A workable policy: the **edge** carries a platform-wide default that rejects abuse as early and as cheaply as possible; the **server** keeps a slightly looser cap purely to protect its own buffers, so it is never the layer that shapes normal policy; the **framework** applies the tightest, per-route limit where the application actually knows what a legitimate request looks like. Exceptions - upload routes - are explicit overrides at the edge and in the application, documented together. What makes this hard is that a rejection below the seam is answered in a format the application never wrote and appears in no application log, so the edge should normalise error bodies and every layer's rejections must be observable.

go deeper

for a junior

Know that more than one layer can reject a request for size, and that the strictest one wins regardless of what your application configured.

for a middle

Explain what each layer's cap protects: the edge protects the network, the server protects its parser buffers, the framework protects the route's expectations.

for a senior

Diagnose from disagreement: an edge looser than the origin hides failures in successful forwards, a stricter edge empties your logs, and equal caps make which layer answers nondeterministic.

for a principal

Own the numbers as one reviewed set with a documented override path, normalise error shapes at the edge, count rejections per layer, and design uploads so the decisive limit fires before any response can be committed.

## Why this is a judgment question There is no single correct place for a limit, because each layer caps for a different reason: - the **edge** caps to keep abusive traffic off the network and away from origins; - the **HTTP server** caps to protect its own parser buffers from exhaustion, which it must do whatever anyone else decided; - the **framework** caps because it is the only layer that knows a search endpoint should never see a ten-megabyte body while an upload endpoint should. Removing any of them removes a protection the others do not provide. So the real decision is the **relationship between the numbers** and who is allowed to change each one. ## A defensible default policy 1. **Edge strictest, as the fleet default.** Rejecting at the edge is the cheapest rejection available and keeps the payload off internal links. This is where a platform team should express "no service here accepts more than X". 2. **Server slightly looser, as a backstop.** Its cap exists so the process survives traffic that bypassed the edge; it should not be the layer that defines business policy, because its errors are the least controllable. 3. **Framework tightest, per route.** The application knows which routes take bodies at all. A tight default with explicit per-route exceptions turns "how big can a request be" into a reviewable decision instead of a global constant. 4. **Exceptions are paired changes.** An upload route needs a raised limit at the edge *and* in the application; a change in one place alone produces a rejection the other layer's owner cannot explain. ## What goes wrong when the layers disagree | disagreement | symptom | why it is hard to diagnose | |---|---|---| | edge looser than origin | a request the edge forwarded is refused at the origin | the edge logs a successful forward; the failure is attributed to the service | | edge stricter than the application expects | a legitimate upload fails identically for every client | the application never sees the request, so its logs are empty | | framework cap tighter than a documented contract | works in tests, fails in production | tests call the entry point directly and never cross the layers that enforce caps | | caps equal at two layers | either may win, nondeterministically | the error format changes depending on which layer trips first | The common thread: a rejection below the seam produces an error body the application did not write and no application-side log entry, so the team that owns the endpoint has the least information about it. ## The streaming-upload problem A body whose length is not declared up front cannot be refused cleanly. The cap is enforced as bytes stream in, and by then the application may already have committed a response - after which the status can no longer become an error. The client then sees a truncated response or a reset rather than a clear rejection. Two mitigations belong to the policy: - prefer the client announcing the length and, where available, asking permission before sending, so refusal happens before the payload travels; - keep the *edge* limit the one that actually bites for uploads, because rejecting before the body reaches the origin avoids the late-commit problem entirely. ## Making it a fleet property rather than a per-service accident - Publish the caps as a **platform default with an explicit override mechanism**; anything else drifts into every service having a different number nobody chose. - **Normalise error bodies at the edge** so that a client gets the same error shape whether the rejection came from the edge, a server or an application - otherwise clients parse three formats. - **Emit metrics for rejections at every layer**, labelled by layer and by route. A rejection nobody counts is a silent outage for the clients hitting it. - **Test across the layers, not just the seam.** Entry-point tests never exercise a cap, so limit behaviour must be verified against a deployed path. - **Review the numbers as a set.** They only mean something relative to each other; a single limit raised in isolation is the classic incident cause. ## What to say in an interview Name all three layers, justify why each keeps a cap, and then say clearly that your contribution is the *ordering and ownership* of the numbers, plus the observability that makes a rejection attributable. A candidate who argues for a single limit in a single place has not accounted for the layers that must protect themselves regardless of policy.

  • Why is a single limit in a single place not enough?
    Because each layer caps for a different reason. The server must protect its own parser buffers whatever the edge decided, the edge must keep abusive payloads off internal links, and only the application knows which routes legitimately accept large bodies. Removing any of them removes a protection the others do not offer.
  • How do you make rejections that happen below the seam attributable?
    Emit a counter at every layer labelled by layer and route, normalise the error body at the edge so clients see one shape, and include a correlation identifier in rejections so an edge-side refusal can be tied to a client report. Without this the owning team sees empty application logs and a client insisting the request was sent.
  • Which layer should own the limit for an upload endpoint?
    The edge should carry the number that actually bites, so an oversized upload is refused before the body reaches the origin and before any response can be committed. The application still keeps its own per-route limit as a correctness check, and the two are raised together as one reviewed change.

saying these in an interview costs you the question

  • Argues for one global limit and removing the others
  • Sets caps per layer without relating the numbers to each other
  • Assumes an oversized streamed upload can always be refused cleanly
  • Leaves rejections below the application uncounted and unlogged
  • Raises an upload limit in one layer only and calls it done
  • Verifies limits with entry-point tests that never cross a layer