Reading HTTP status codes as operational signals
Understand what a response code establishes, how intermediaries affect it, and when a client can retry safely.
An HTTP status code is a compact statement about one response. It is useful for dashboards and application logic, but it is not a complete explanation of an incident. A browser can receive a successful response containing the wrong content, and a reverse proxy can generate an error before the application receives the request. Good debugging combines the code with the method, headers, response body, and identity of the component that produced it.
Begin with the response class
The first digit groups status codes into informational responses, success, redirection, client errors, and server errors. These classes provide a starting point rather than a blame assignment. A 4xx response may reflect a malformed request, missing authentication, or an intentional access policy. A 5xx response can originate in an intermediary rather than the application server. MDN's HTTP status reference provides the standard code inventory.
Always retain the exact code. Collapsing every failure into “HTTP error” loses distinctions that influence the next action. Also distinguish an HTTP response from failure to establish a connection: DNS failure, a TCP timeout, or rejected TLS verification may produce no HTTP status at all.
Inspect a real response without hiding details
For a public resource that is safe to fetch, capture headers and timing:
curl --silent --show-error --max-time 15 \
--dump-header response-headers.txt \
--output response-body.txt \
--write-out 'status=%{http_code} total=%{time_total}\n' \
https://example.com/
This example sends a GET and stores the response for inspection. The curl manual documents the output options and time limit. Review stored headers before sharing them: cookies and personalized data may appear. Do not include a real API token in a copied command or public incident report.
Avoid automatically adding --location during the first inspection. Following redirects can hide the original response and its Location header. A HEAD request is convenient for some checks, but an application's HEAD behavior need not prove that the full GET body is correct. Choose the method that corresponds to the symptom.
Treat successful responses according to their contract
200 is not the only successful code. A creation endpoint can return 201, and 204 carries no response content. Conditional requests can produce 304, directing the client to use a stored representation rather than supplying a new body. Code that unconditionally parses every accepted response as JSON may therefore fail despite correct server behavior. RFC 9110 defines these response semantics.
For an operational check, validate something meaningful in addition to the code. Confirm the expected content type, a small known body marker, or an application-specific health field. Keep the check lightweight and separate public availability from private administrative health. A login page returning 200 should not be mistaken for a successful authenticated API response.
Preserve context around redirects and client errors
When investigating a redirect, record its destination, whether it stays on the expected host, and whether the client changed the request method. Look for loops involving HTTP-to-HTTPS rules or competing canonical-host rules. A redirect can be intentional and still be incompatible with a particular client workflow.
For authentication and authorization errors, compare an authenticated request with a deliberately unauthenticated control. Check the credential's scope and expiration without printing it. Repeatedly resending the same invalid secret is unlikely to repair a policy rejection. For a missing resource, verify the exact path and deployment version before assuming the entire service is unavailable.
These comparisons should remain bounded. Do not spray guessed URLs or credentials against a service while diagnosing one request. A small reproducible request is more useful than a large volume of inconsistent attempts that obscures the logs.
Retry rate limits with a budget
A 429 response indicates rate limiting. The MDN description of 429 notes that implementations may count requests by client, account, or another criterion. A retry storm can worsen the problem for every request sharing that identity.
If supplied, Retry-After can specify either a delay in seconds or an HTTP date; see the Retry-After reference. Honor a valid instruction within the client's overall deadline. Use a finite attempt budget and randomized backoff when designing automatic recovery. Decide separately whether the operation is safe to repeat, especially when the first request might already have created or changed data.
Locate server errors across the request chain
Draw the actual chain: client, edge, reverse proxy, application, and dependencies. For a gateway error or timeout, compare access logs with upstream connection records at the same timestamp. A response header or branded error body may suggest which component answered, but confirm that suggestion in the component's logs.
A useful incident note records the request identifier, exact status, method, sanitized path, elapsed time, and producing component. It also states whether a retry succeeded and under what conditions. This turns a status code into evidence that can guide a repair, while avoiding the false precision of naming an upstream fault from a three-digit number alone.