Status codes and redirects
Every HTTP response opens with three digits, and the first of them is the one that matters. A client that understands only the first digit still behaves correctly, which is exactly why the ranges are grouped the way they are.
| Range | Means | What a client should do |
|---|---|---|
| 1xx | Still going | Keep waiting |
| 2xx | It worked | Use the response |
| 3xx | It is somewhere else | Go there instead |
| 4xx | Your request was the problem | Fix the request; retrying won’t help |
| 5xx | Our end was the problem | Retry may well help |
The 4xx/5xx split is a statement about blame, and it is the most useful distinction in
the whole list: it tells a client whether retrying is sensible. That is why a request
that parsed cleanly and then said something impossible gets 422 rather than 400, and why
a method that is simply wrong for an existing resource gets 405 — with an Allow field
naming what would have worked.
Redirects: four codes that disagree about POST
A 3xx says the thing you want is elsewhere and Location says where. The cost is a
whole extra round trip before anything useful is served, and a chain of them
costs one per hop.
The codes differ on two axes — whether the move is permanent, and what happens to a POST:
| Code | Permanent? | A POST becomes |
|---|---|---|
| 301 | Yes | A GET (historically) |
| 302 | No | A GET (historically) |
| 303 | No | A GET, deliberately |
| 307 | No | A POST, preserved |
| 308 | Yes | A POST, preserved |
The "historically" is not a footnote. Early browsers rewrote POST to GET on 301 and 302 in defiance of the specification, enough of the web came to depend on it that the behaviour was standardised, and 307 and 308 exist purely to provide the codes that mean what 302 and 301 were supposed to mean.
Key takeaways
- The first digit carries the meaning; a client that reads only that still behaves correctly.
- The 4xx/5xx split tells a client whether retrying can possibly help, which makes it the most operationally useful distinction in HTTP.
- Returning 200 with an error in the body lies to every cache, proxy and retry policy on the path.
- Each redirect is a full extra round trip, and chains multiply it.
- 301 and 302 turn a POST into a GET because browsers did it first; 307 and 308 exist to mean what those two were meant to mean.