HTTP errors: a debugging field guide
How to triage 4xx vs 5xx, what 502/503/504 each tell you about your stack, and the client errors that waste the most time.
First digit first
The first digit of a status code assigns blame. 4xx means the request is wrong — fix the client. 5xx means the server failed to serve a valid request — fix the server or its dependencies. 3xx means the resource moved and the client should follow. Retrying a 4xx without changing the request is pointless; retrying a 5xx with backoff is often exactly right.
The 4xx that waste the most time
- 401 vs 403 — 401 means authentication is missing or failed (no valid token); 403 means you are authenticated but not allowed. A 403 will not be fixed by logging in again.
- 404 on an API you just deployed — check routing and base path before assuming data is missing.
- 405 — the endpoint exists but not for that method; a POST to a GET route.
- 409 — the request conflicts with current state: duplicate unique key, edit conflict, or a version mismatch.
- 413 / 431 — the body or the headers exceed a limit, often set by a proxy you forgot about.
- 422 — well-formed but semantically invalid: validation errors, distinct from 400's malformed syntax.
- 429 — rate limited. Read Retry-After, back off, and stop hammering.
The reverse-proxy trio: 502, 503, 504
Behind a load balancer or reverse proxy, three 5xx codes report different upstream failures. 502 Bad Gateway: the proxy reached the upstream but got an invalid response — the app crashed, returned garbage, or closed the connection. Check whether the app process is actually running and reading its port. 503 Service Unavailable: the upstream is known-unavailable — overloaded, restarting, deploying, or deliberately in maintenance. Check capacity, health checks, and deploy pipelines. 504 Gateway Timeout: the upstream accepted the request but did not respond within the proxy's timeout. Something is slow — a database query, a downstream HTTP call, a hung worker.
The triage order follows naturally: 502 asks 'is the app up and talking correctly?', 503 asks 'is the app willing and able?', 504 asks 'what is it waiting on?'
A debugging loop that converges
Reproduce the exact failing request — the same method, path, headers, and body; curl from the server itself if you can, which separates network layers from app layers. Read the response body, not just the code: frameworks and proxies leave fingerprints there. Then walk the chain outward: app logs for 5xx, middleware and auth config for 401/403, proxy config for 502/504, rate-limit rules for 429. Most 'mysterious' HTTP errors resolve within two hops of this loop.
References
Related tools
- JWT DecoderDecode JWT headers and payloads locally — no signature verification.
- URL Encoder / DecoderEncode and decode URL components and query strings safely.