What do http.Error and http.Redirect write to a response besides the status code?
answer
- each one writes a whole response
- one forces a header you may not want
- the handler still has to return
- text/plain and nosniff, or Location
basics
~10 shttp.Error clears Content-Length, forces Content-Type to text/plain; charset=utf-8, adds X-Content-Type-Options: nosniff, sends the status, then writes the message and a newline. http.Redirect sets Location and, for a GET, writes a small HTML link page.
solid answer
~40 s`http.Error(w, msg, code)` is a full response, not just a status: it deletes any Content-Length you set, overwrites Content-Type with `text/plain; charset=utf-8`, adds `X-Content-Type-Options: nosniff`, calls `WriteHeader(code)`, and writes the message followed by a newline. That overwrite is why a JSON API cannot use it and still return JSON — most services write their own error helper instead. `http.Redirect(w, r, url, code)` sets the Location header — the url may be a path relative to the request path — sends the status, and for a GET writes a tiny HTML page containing the link, unless the handler already set a Content-Type. Both call `WriteHeader` themselves, so they must be the first write, and crucially neither ends the request: the handler must `return` afterwards or its next write will land on top of them.
code
go · 6 linesfunc writeJSONError(w http.ResponseWriter, code int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(map[string]string{"error": msg})
}go deeper
Remember that both helpers send a complete response and that you must return right after calling either one. Know that http.Error's body is plain text.
List what each helper touches: Content-Length, Content-Type and nosniff for http.Error; Location and a conditional HTML body for http.Redirect. Explain why both must precede any other write.
Show the production instincts: a project-wide JSON error helper instead of http.Error, generic messages with the detail logged rather than echoed, and a review rule that every error branch ends in a return.
Own the error-response contract across services: one body shape, one place that writes it, and a deliberate decision about how much internal detail ever crosses the boundary. Ad-hoc http.Error calls are how two services end up with two error formats.
### These helpers are not shorthand for a status code Both `http.Error` and `http.Redirect` write a **complete response**: headers, status and body. Treating either as "just set the status" is the source of most bugs around them. ### http.Error, step by step ```go func Error(w ResponseWriter, error string, code int) ``` What it does, in order: 1. **Deletes Content-Length** from the header map. Any length you had computed for the response you were *going* to send is wrong now, and leaving it would produce a response the client cannot read. 2. **Sets Content-Type to `text/plain; charset=utf-8`**, overwriting whatever was there. 3. **Sets `X-Content-Type-Options: nosniff`.** The message may contain arbitrary text, and this stops a browser re-sniffing it as HTML. 4. **Calls `WriteHeader(code)`.** 5. **Writes the message plus a trailing newline.** Three practical consequences: - **It must be your first write.** Because step 4 sends the status line, calling `http.Error` after you have already written body bytes cannot change the status — the earlier implicit or explicit status stands, the server logs a superfluous `WriteHeader` call, and the message is appended to whatever partial body is already on the wire. - **It does not return from your handler.** The documentation is explicit that it does not otherwise end the request and the caller must ensure no further writes happen. Forgetting `return` after `http.Error` is the classic Go handler bug. - **It is not JSON-friendly.** Step 2 overwrites `application/json` unconditionally. An API whose clients parse every response body needs its own helper. A typical replacement, which keeps the same shape but the right type: ```go func writeJSONError(w http.ResponseWriter, code int, msg string) { w.Header().Set("Content-Type", "application/json") w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(code) _ = json.NewEncoder(w).Encode(map[string]string{"error": msg}) } ``` Note that the message you pass to either helper is echoed to the client. Passing `err.Error()` straight through is how internal detail — a file path, a query, a host name — leaks into a response; log the detail and send a generic message. ### http.Redirect, step by step ```go func Redirect(w ResponseWriter, r *Request, url string, code int) ``` 1. **Sets the Location header** to the target, escaping non-ASCII characters. The url may be a path relative to the request path, and Go resolves it for you rather than making you build an absolute URL. 2. **Sets Content-Type to `text/html; charset=utf-8`** — but only if the handler had not already set a Content-Type. 3. **Calls `WriteHeader(code)`.** 4. **For a GET request only**, writes a short HTML body containing an anchor to the target, so a human looking at the raw response, or a client that does not follow redirects, still has the link. A HEAD or POST redirect gets the header and status with no body. Like `http.Error`, it writes the status itself, so it has to come before any body write, and like `http.Error` it does not end the handler — you `return` after calling it. ### The shared shape Both helpers exist because the header-then-status-then-body ordering is easy to get wrong by hand, and both encode a complete, correct small response. The two rules that make them safe are the same: - **Call them before anything else has been written.** - **`return` immediately afterwards.** A handler with three error branches and one success branch should have four `return` statements, and a reviewer can check that by eye. If you find yourself wanting to call `http.Error` at the end of a function that has already produced output, the handler needs restructuring, not a different helper: build the body in memory, decide the outcome, and only then write. ### Choosing between them and hand-writing the response Use `http.Error` when a plain-text error body is acceptable — internal tooling, a health endpoint, a static file server. Use your own helper when clients parse the body. Use `http.Redirect` whenever you would otherwise hand-set Location, because getting the relative-path resolution and the escaping right by hand is fiddly and the free HTML body is genuinely useful when debugging with a raw client.
- Why must a handler return immediately after calling http.Error?`http.Error` writes the status and body but does not end the request. Anything the handler writes afterwards is appended to the error body, and a second status attempt is dropped with a superfluous `WriteHeader` warning in the server log. The missing `return` is the classic version of this bug.
- Why can't a JSON API use http.Error directly?It unconditionally overwrites Content-Type with `text/plain; charset=utf-8` and writes the raw message as the body, so a client that parses every response gets plain text where it expected an object. Services write a small helper that sets the JSON type, adds nosniff, writes the status, and encodes an error object.
- Does http.Redirect behave differently for a POST than for a GET?It sets Location and the status for any method, but it only writes the little HTML anchor body for GET. It also leaves Content-Type alone if the handler already set one, so a handler that had declared JSON does not suddenly claim to be HTML.
saying these in an interview costs you the question
- Thinks http.Error returns from the handler
- Expects a JSON Content-Type to survive an http.Error call
- Calls http.Error after already writing part of the body
- Thinks http.Redirect only sets a header and no body
- Passes err.Error() straight into http.Error as the client message