skip to content

Response Object & Commit

Status, headers and body, the commit point that freezes headers, buffered versus flushed bodies, redirects and post-response hooks. Asked because 'headers already sent' bites everyone.

on this pageshow

questions

5

In a server-side web framework, why must a handler set the response status, headers and cookies before writing body bytes?

level: juniorimportance: must knowfreq 66%

answer

  1. the wire has an order
  2. status line, then headers, then body
  3. writes fill a buffer first
  4. cookies are just response headers
  5. after the first flush, changes are lost

basics

~20 s

An HTTP response carries its status line and header block ahead of the body, so the framework sends them first. Once body bytes are flushed the response is committed, and later status, header or cookie changes are lost.

solid answer

~40 s

The order is forced by the message format, not by style. An HTTP response is a status line, then a header block, then a blank line, then the body, so whatever the framework has to send first must be decided first. Most frameworks hide this behind a response object with a write buffer: your status and headers sit in memory while body bytes accumulate, and nothing goes out until the response is *committed*. Commit happens when the buffer fills, when you flush explicitly, or when the handler finishes and the framework writes the response out. Because a cookie is just a `Set-Cookie` header, it obeys exactly the same rule. Setting status, headers and cookies before any body write is the only ordering that is safe regardless of payload size.

go deeper

for a junior

Remember the shape of an HTTP response: status line, headers, blank line, body. Decide status, headers and cookies first, write the body last, and you will never hit this class of bug.

for a middle

Be able to explain the write buffer: header changes are cheap until the first flush, and the flush may be triggered by body size rather than by anything you wrote deliberately.

for a senior

Watch for handlers whose behaviour depends on payload size, and for error handling that tries to set a status after content is already streaming. Put fallible work before the first write.

for a principal

The durable fix is structural: a shared response-building step that decides status and headers before any body work, so no team discovers the ordering rule from a size-dependent production incident.

## The message format decides the order An HTTP response is not a bag of fields that a server fills in any order it likes. On the wire it is a fixed sequence: 1. a **status line** carrying the protocol version and the status code; 2. a **header block**, one field per line, including every `Set-Cookie` the response carries; 3. a blank line; 4. the **body** bytes, if the response has any. A server writes those bytes in that order because that is how the receiver parses them: a client reads the status, reads headers until the blank line, and only then knows how to read the body. Nothing about that changes in later protocol versions; they encode the same three parts differently but still deliver metadata before content. So the rule "status and headers before body" is not a framework convention you could argue with. It is the shape of the message. ## What the framework does with your calls Most server-side web frameworks give a handler a **response object** with roughly three groups of operations: set the status code, set or add headers and cookies, and write body content. The first two groups mutate in-memory fields. The third appends to a **write buffer**. While the buffer still has room, nothing has been sent. Your status is a mutable field, your headers are a mutable map, and you may keep changing them. That is why code that sets a header *after* a small write usually appears to work, and why the bug it hides is so easy to miss. The response leaves that mutable state when it is **committed** — the moment the first bytes actually go to the client. Commit is triggered by any of: - the buffer filling up because the body is bigger than it, so the framework must drain it; - an explicit flush, which forces the commit whether or not the buffer is full; - the handler finishing normally, after which the framework writes status, headers and whatever the buffer holds; - a redirect or an error shortcut, which in many frameworks writes the response immediately. ## Why cookies are not special A cookie is delivered as a `Set-Cookie` response header. It is stored in the same header structure, serialized in the same block, and frozen at the same instant. A handler that writes a page and then decides to attach a session cookie is making a header change, with all the same timing risk as adding any other field. Frameworks that expose a dedicated cookie helper hide the header, not the constraint. ## What goes wrong when the order is reversed | What you do | Small body (fits the buffer) | Large body (overflows the buffer) | |---|---|---| | Set status, then write | Correct status sent | Correct status sent | | Write, then set status | Appears to work | Status change lost or an error raised | | Write, then add a cookie | Cookie is sent | Cookie silently never reaches the client | | Write, then flush, then set a header | Header change lost | Header change lost | The dangerous row is the one that depends on size. A handler that renders a short result in development and a long one in production changes behaviour without any code change. Frameworks differ in how loudly they complain: some raise an error on a post-commit header change, some log a warning, some discard it in silence. You cannot rely on being told. ## The habit that makes it a non-issue Do all fallible and all decision-making work before the first byte: - resolve authorization, look up data and validate input first, so a failure can still choose its own status; - compute the status, content type, caching-related headers and cookies while nothing has been written; - write the body last, ideally in one place; - treat a redirect helper as terminal — return immediately after calling it rather than writing more. Stated as a single rule: **decide the response, then produce it**. That ordering is free when the body is small and it is the only thing that works when the body is large, which is exactly why interviewers use it as a screening question.

  • What happens if a handler sets a header after the response has already been committed?
    It has no effect on what the client receives, because those bytes are gone. Frameworks handle the attempt differently: some raise an error, some log a warning, some discard it silently. Never rely on being warned; treat any post-write header change as a bug.
  • Is setting a cookie different from setting any other response header at commit time?
    No. A cookie is carried in a `Set-Cookie` response header, stored with the other headers and serialized in the same block, so it is frozen at exactly the same moment. A cookie helper is convenience over the header, not an escape from the ordering rule.
  • When a handler issues a redirect, what has it done to the response object?
    A redirect helper sets a 3xx status and a `Location` header, and in most frameworks writes the response out straight away, often with a tiny or empty body. After that the response is committed, so later status or header changes are lost. Return immediately after redirecting.

saying these in an interview costs you the question

  • Believes headers can be added any time before the handler returns
  • Says a cookie set after the body still reaches the client
  • Assumes the framework always buffers the whole response, whatever its size
  • Thinks a lost header change is a framework bug rather than an ordering error
  • Expects an error every time a header is set too late
open as a page

In a web framework, what does it mean that a response has been committed, and what does commit freeze?

level: middleimportance: must knowfreq 74%

basics

~20 s

A response is committed once its first bytes reach the client. Commit freezes the status line and every header, cookies included; only body bytes may still be appended. Buffer overflow, an explicit flush or the handler ending triggers it.

open as a page

A handler fails halfway through writing a large response and the client receives a success status with a truncated body instead of your error page. Why, and how do you fix it?

level: seniorimportance: must knowfreq 58%

basics

~20 s

The response committed before the failure, so the success status and headers were already on the wire and the error stage had nothing left to set. Do fallible work before the first byte, and give streamed responses a completeness signal.

open as a page

In a web framework, what decides whether a response can declare its content length, and how does flushing affect that?

level: middleimportance: should knowfreq 54%

basics

~20 s

A framework can declare a length only if it knows the total body size when the header block goes out. That holds when the whole body is still buffered; an early flush commits first, so the response is delimited instead.

open as a page

How would you set response buffering and commit policy across services that return both small payloads and very large exports?

level: principalimportance: should knowfreq 42%

basics

~20 s

Make late commit the default: size the buffer above the common payload so ordinary responses stay replaceable by an error response, and make early flushing an opt-in that owes a completeness signal and a post-commit failure metric.

open as a page