
Choosing an HTTP Status Code - wtbob
http://racksburg.com/choosing-an-http-status-code/
======
parent5446
Some things I don't particularly like in this diagram:

* I don't consider 307 and 308 to be irrelevant. Even if many browsers today are safe against method-change attacks, it's always better to be explicit.

* I don't see why 304 would only be used if "implementing a web server". Many web applications do their own Last-Modified or E-Tag checking behind the web server (and for good reason).

* There are also non-webserver reasons you'd use 408 and 413.

Also

    
    
        > I don’t find this argument compelling, if for no other 
        > reason than this: in a world where everyone is moving
        > to HTTPS, we’ve forbidden any proxy or caching nodes 
        > that are not under direct control of the server.
    

What? Even if you are controlling the cache, I don't see why you would want to
make your job more difficult by having to explicitly control it when HTTP can
do much of the job for you. (Not to mention there are cases, specifically AWS
and cloud services, where you wouldn't have direct control of your proxy
cache, but are still using HTTPS.)

Otherwise I'm glad to see some people still think using a variety of HTTP
status codes appropriately is a good idea. It makes services easier to use and
more intuitive.

~~~
spacecowboy_lon
Don't get clever and use these wacky non standard status codes like 307 308
303 your at the mercy of Google as to how they will treat them.

99.9% of the time use a 301 redirect.

~~~
BillinghamJ
They're not non-standard, and any unrecognized 3xx codes are treated as 300.

~~~
spacecowboy_lon
And you know _exactly_ how google, bing et al handle this situation.

------
TheAndruu
Hard to get behind an argument on status codes being pointless when the
article itself cites the usefulness of wisely chosen status codes, such as
"201 Created, 429 Too Many Requests, and 503 Service Unavilable".

I've created and used many a RESTful API and can vouch the ones that just
return 200 even in cases of error are a big pain in the neck.

This is usually done (IMO) as a shortcut on the hand of the service provider,
lack of knowledge on the conventional codes that should be used, or for the
sake of more flexibility when endpoints change.

However, all of those reasons just push more work onto the service consumers.
Lack of standardized status codes means the consumer must have customized
error detection, which is more susceptible to changes as the provider pushes
out updates.

~~~
anExcitedBeast
HTTP status codes are also useful for infosec people during breach
investigations. Sometimes all that's available is access logs, and those codes
can provide a lot of context.

~~~
TheAndruu
Good point. They're also critical for any HATEOS applications

------
peteretep

        > I’m not completely sure they do matter.
        > There’s a lot of smart people at Facebook and they
        > built an API that only ever returns 200.
    

This is the best bit of the article.

Most (many?) APIs, whether or not their authors call them REST, are single URL
APIs that you throw JSON/YAML/whatever at. If the caller is going to get back
a serialized status message (["Not found"]), then the HTTP request was
successful, and it should return 200.

I'm not sure why people insist on spreading their APIs out between the
transport and content layer.

~~~
parent5446
If the API is indeed RESTful, and it complies with the concept that a URI
represents an address of a specific resource, then returning 200 even when the
resource was not found does not make any sense. The request was not
"successful", rather the request failed to find what it was looking for!

That said, "single URL APIs that you throw JSON/YAML/whatever at" are not
RESTful anyway. For a non-REST API, then it may make sense to return 200 even
if something was not found, but it is still not a good idea to just ignore the
properties of the upper-level protocol your data is ferried on.

    
    
        > I'm not sure why people insist on spreading their APIs out between the transport and content layer.
    

HTTP is not the transport layer. It's an application protocol meant to convey
application data. People spread their API over it because that's how it's
supposed to be used. If you're just using HTTP as a means to toss random data
between computers, you should consider TCP.

~~~
chromakode
On the client side, whether a GET returned a successful (2xx) or error
(4xx/5xx) status is observable for cross-origin requests. This can leak
information about the user, particularly if you have cookie-authenticated
resources that 503 depending on the user's identity (e.g. Facebook 503ing if
you're not friends with X). This can be resolved by requiring a CSRF token or
fancy header, but that muddles the RESTful semantics.

A common solution is to make your public API RESTful and authenticated
differently from your browser cookie sessions, and make the private web APIs
always return 200.

~~~
TheAndruu
403 would be the proper status if the user was authenticated but forbidden.
503 would imply an error on the server and that the service is unavailable.

Here the 403 is appropriate because the user is logged in and trusted to some
degree by your system, but isn't allowed to access that URI.

If the user were unauthenticated and tried to access the same URL, he should
get a 401 for Unauthorized, which is the same response he should get for every
URI in your system, thus exposing nothing about your underlying service.

~~~
nitrogen
GitHub returns 404 instead of 403 to prevent these information leaks.

~~~
dragonwriter
> GitHub returns 404 instead of 403 to prevent these information leaks.

This behavior is explicitly permitted by the standard, FWIW: _An origin server
that wishes to "hide" the current existence of a forbidden target resource MAY
instead respond with a status code of 404 (Not Found)._

[https://tools.ietf.org/html/rfc7231#section-6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)

------
Asmod4n
I can only recommend webmachine here,
[https://raw.githubusercontent.com/webmachine/webmachine.gith...](https://raw.githubusercontent.com/webmachine/webmachine.github.io/master/images/http-
headers-status-v3.png). (it starts at b14)

This is actual code drawn out as a state diagram. It's officially available
for Erlang and Ruby. (Disclaimer: I am one of the "maintainers" of the Ruby
version)

~~~
jalfresi
The more up to date version of this diagram is here:

[https://github.com/for-GET/http-decision-diagram](https://github.com/for-
GET/http-decision-diagram)

~~~
Intermernet
Still Missing:

Are you a Teapot? Yes: Error 418.

~~~
andreineculau
submit a pull-request :)

~~~
Intermernet
Unfortunately they don't claim to conform to RFC 2324. If they did, I would
:-)

------
andreineculau
It's good to see more diagrams/flowcharts on the matter, but beyond that - for
those that understand the power of abstraction between layers, but you think
there should be a simple straightforward to spit out the correct http status
code while you worry about the semantics alone: [https://github.com/for-
GET/http-decision-diagram](https://github.com/for-GET/http-decision-diagram)

* an old PoC in NodeJS exists that reads the states/transitions from a JSON file and calls the correct callbacks in the correct order. Implement the callbacks (semantics) and you're done.

Disclaimer: I'm the author.

------
rplnt
I really love Atlassian APIs. JSON request gone wrong? Here you have an error
in XML! But I geuss that's expected when your documentation only has XML
examples.

What about a request for non-existing resource (a file)? That's a 200 OK with
an HTML for you! Also, the content of the HTML is "<h1>200 OK</h1>".

Luckily most of the functionality in their products is not implemented through
the APIs...

~~~
fluxquanta
>I really love Atlassian APIs. JSON request gone wrong? Here you have an error
in XML!

There's a company I work with that is similar to this: Requests are XML,
successful responses are returned as JSON with status 200 (with an empty error
status field), errors are returned as XML with status 200, and the error text
node is always "An error occured [sic] with your request".

------
daurnimator
Useful resource for status codes:
[https://httpstatuses.com/](https://httpstatuses.com/) which I always remember
better as [http://httpstatus.es/](http://httpstatus.es/)

More humourous alternative:
[http://httpstatusdogs.com/](http://httpstatusdogs.com/)

~~~
andreineculau
Alternative counterparts, based on know-your-http-well[1] :

* [http://bin.hyperrest.com/](http://bin.hyperrest.com/) * [http://httpflies.com/](http://httpflies.com/)

[1]: [https://github.com/for-GET/know-your-http-well](https://github.com/for-
GET/know-your-http-well)

------
3pt14159
Edit: This comment comes off as overly critical. I really do love these
diagrams, I've just had this argument so many times with people I find it
frustrating.

I completely disagree with this part of the diagram:

[https://imgur.com/HZS3cVy](https://imgur.com/HZS3cVy)

If the resource is secret then the entire guessable path should return a 401
or a 403, not a 404. Knowing whether something exists is a _privilege_ that a
user needs to authenticate in order to gain. This isn't just an academic
distinction. Github does 404s for unauthenticated requests and python's
default http client (at least for python 2) doesn't send credentials unless it
hits a 403 or a 401. Since github sends a 404 you have to hack around the
language.

404 means Not Found. But that isn't true. The real status code is Forbidden
and it should be used on all possible urls that follow a guessable pattern.

~~~
mjpuser
I think that you have a point as far as staying true to what the status codes
mean, but it comes with a price. To make the resource invisible to the user by
404ing instead of 403ing, you protect yourself from people searching for
endpoints that return personal info... and all urls are guessable. If you get
a hit on some resource in a sea of 404s, you are that much closer to gaining
private info.

~~~
3pt14159
But if there is a chance that a resource exists at all, then that whole range
of URLs should return 403, regardless of whether or not they exist.

------
aaronbasssett
In general terms:

* 2xx - We've got it!

* 3xx - They've got it

* 4xx - You fucked up

* 5xx - We fucked up

~~~
jstimpfle
Some 4xx are "you fucked up", but I would say more of them are "sorry, not
possible" or "sorry, transaction failed".

------
mnot
Not bad. The ordering of 4xx status codes isn't that deterministic (often,
it's situational), and last time I checked, Twitter uses 429 instead of 420
now (they like standards).

------
ungrim97
The for get http repo already looks to provide a http status flow diagram at
[https://github.com/for-GET/http-decision-diagram](https://github.com/for-
GET/http-decision-diagram).

Its a version 4 currently

------
Sujan
That's gold: "Are you rage-quitting the internet? ---Yes---> 410 Gone"

~~~
mstade
410 Drop The Mic

I'd buy that for a dollar.

------
mkobit
This is the first time I have really noticed the '405 Method Not Allowed'
code. This makes it seem like you should explicitly handle every verb, when in
cases before I have just left them unimplemented (which typically just results
in a 400). Is it considered a best practice to handle all of these other verbs
with a '405'?

~~~
wylee
I personally think it's best practice, although returning a generic 400 isn't
_that_ bad. Libraries like Django REST Framework handle this automatically.

------
cletus
Some of the comments here are positing a false dichotomy. The choices aren't
between returning 200 with an error code and returning an HTTP code for each
error.

I've gone down the path of trying to shoe horn every error condition into a
separate HTTP status code and (IMHO) it's a Big Mistake [tm]. You really are
confusing transport and application layers and the transport layer simply
doesn't have enough codes for what you need.

When something obviously fits into an HTTP status code then sure, use it,
particularly when it'll mostly make your client do something sensible.

But you just can't get away from needing error codes that go beyond HTTP
status codes for any moderately complex Web application.

------
jalfresi
I seem to recall that this flow chart covered all the bases pretty thoroughly:

[https://github.com/for-GET/http-decision-diagram](https://github.com/for-
GET/http-decision-diagram)

------
enobrev

        "the existing status codes are much too general for a modern website/API... why worry about spending any time on a redundant, not-as-useful HTTP status code?
    
        When pressed for a reason why it is important to use specific status codes, a common reason cited is that HTTP is a layered system and that any proxy, cache, or HTTP library sitting between the client and server will work better when the response code is meaningful. I don’t find this argument compelling, if for no other reason than this: in a world where everyone is moving to HTTPS, we’ve forbidden any proxy or caching nodes that are not under direct control of the server."
    

I do find this reasoning to be perfectly valid and have implemented many APIs
accordingly for quite some time. It has a lot to do with the first place to
look when something goes wrong. Does the 404 mean I mistyped the URL or that
the Application can't find the Resource? Did the endpoint crash or did I mess
up the rewrite in the nginx config?

That said, Michael's counter-argument is well worth consideration. I've
generally left HTTP status codes and Application status codes completely
separate for any sizable project, but I find this article and argument
worthwhile and will continue to reconsider, as I generally do upon every new
project, whether using HTTP status codes to represent general application
state is worthwhile.

The original point regarding complexity is most important to me, but Michael's
diagrams do an excellent job of showing how Application state can be
considered an extension of the Protocol in specific cases.

------
cel1ne
Slightly off-topic, but I found this article useful when implementing a REST-
API:

[http://www.vinaysahni.com/best-practices-for-a-pragmatic-
res...](http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api)

It has a section about status-codes: [http://www.vinaysahni.com/best-
practices-for-a-pragmatic-res...](http://www.vinaysahni.com/best-practices-
for-a-pragmatic-restful-api#http-status)

------
lukiebriner
I agree with parent5446 - there are various codes that although they are
_usually_ only used by web servers, they are also used in some REST APIs,
including mine.

e.g. I return 304 when people query an image 1) Because it is not a static
resource but is pulled in from another server and 2) It will never expire
because if it changed, a parameter in the URL would also change.

------
virmundi
I've been down in the weeds a bit when it comes to authorization. Every
resource in my system has its group information in an attribute {:rights {:c
[..list of groups], :r [..] :u [..] :d [..]} ....rest of resource...}. In
parallel with the resource as above, there is an index table for all of the
resources in the system. When a resource, our group of resources is queried, I
filter the index table with the groups for the current user and join to the
resource table. I can only bring back resources for which the user has rights
for the operation.

This means that I can't ever have a 403. All I can return is a 401 or 404 or a
200 with an empty list. I kept fretting about this. Now I feel better. #how-i-
stopped-worrying-and-learned-to-love-the-atomic-codes.

------
justizin
Anyone who thinks 304 Not-Modified is mostly-irrelevant in 2015 is mostly-
irrelevant.

------
joveian
After looking at magic getopt yesterday I noticed Colin Percival's "The HTTP
500 Soltion" article: [http://www.daemonology.net/blog/2015-11-27-the-
HTTP-500-solu...](http://www.daemonology.net/blog/2015-11-27-the-
HTTP-500-solution.html)

I tend to agree with that and even more so looking at these flow charts.
Complex error handling is an easy source of bugs. At the very least, return
codes should be limited to the minimum information needed to prompt the
desired action in browsers, proxies, and search engines.

------
Cakez0r
What if you need to communicate an error reason with your status code? E.G.
distinguishing between Forbidden because you don't have some permission and
forbidden because you're not friends with some user.

~~~
Rezo
I like to return a error code property for each particular error case in the
JSON body of the response. This allows you to determine exactly where in the
code the error is from, and also allows the client to do fine-grained error
handling in the UI if required. I've also found it incredibly useful to
include a unique error ID with each request that is also logged server side,
so that you can pinpoint the request in your logs and look up the context
including previous and next requests.

It looks something like this:

HTTP 403 { errorCode: 403102, message: "Missing required permission:
IN_SOCIAL_NETWORK", id: "064d6a1f-ba30-47de-8975-c319f01729ca" }

------
MichaelGG
>Postel's law states that one should be conservative in what one does, but
liberal in what one accepts from others; this is great advice for life, but
terrible advice for writing software.

This perfectly sums up what I've tried to say about this "robustness
principle" for years. Fantastic.

SIP inherits these status code ideas, plus adds super-neato whiz-bang headers,
like "Retry-After", that _allow comments_. Their actual example is "Retry-
After: 300 (in a meeting)". It's beyond delusional.

Except.... someone read the spec. They see Retry-After. They then implement it
in some big iron that connects to the PSTN. Little SIP VoIP provider comes
along, passes along this dumb header, Retry-After. Suddenly, big switch guy is
pissed, as Verizon has disabled all his lines as "Retry-After" gets translated
into "shutdown trunk" or something. Awesome. Really a round of applause for
the IETF there. (You have got to know that these headers came about just
sitting around thinking shit up, vs actually trying to implement telephony.
That's why SIP says it might be used "to run a chess game", but has a
mandatory "Call-ID" field. Oops.)

------
blowski
What code should I use when a user submits an invalid value over a form? Most
of the time, I see 400 or 422, or even 403. I've also seen people say it
should be 200, because the HTTP request was successfully handled.

It seems like such a common requirement, I'm surprised there's no agreed
standard.

~~~
mstade
400 as far as I understand it is about the request syntax itself. If the error
is in semantics (i.e. POSTing a phone number where you expected a date of
birth) then the request itself isn't malformed. 403 _might_ be appropriate,
but generally concerns authorization more than the ability to process the
request -- i.e. trying to POST something the user doesn't have authority to
modify would return a 403. If the user is authorized for the specific request,
and the request is valid syntactically, but the semantics of the request body
makes no sense, then the entity of the request can't be processed and 422 is
what you should return.

~~~
blowski
Thank you, I have been using 400, but you're reasoning here makes sense.

------
rix0r
I hate that "Too Many Requests", or "Throttled", or what have you, is a 4xx
status code.

It renders simple middleware logic like "retry on a 5xx, don't retry on a 4xx"
invalid.

If you'd call it "Server Over Capacity" you could just as well argue for a 5xx
code.

~~~
jalfresi
I'm not sure I follow. The client would receive those errors because they are
errors on the clients side; the client should ensure that they are not
flooding a service other wise the server will cut them off.

Your simple middleware logic is going to have to examine the 400 error in more
detail in order to adjust its request rate e.g. exponential back off.

It's not that the "server is over capacity", its that the specific client is
making "too many requests".

------
ap22213
An API is an interface. Like all interfaces, an API has an 'audience'.

Generally, the successful team will try to make their interface as usable as
possible to that 'audience' (considering diminishing returns).

------
ColinWright
See also: HTTP flow chart ready to print [pdf]

[https://news.ycombinator.com/item?id=10695467](https://news.ycombinator.com/item?id=10695467)

------
IshKebab
Rather than saying what error code I should or shouldn't use because of an
out-dated loosely followed spec, maybe tell me based on __what modern browsers
do __.

~~~
rickycook
well browsers are frequently not the important interfaces to an api (other
than the 3xx)

------
latenightcoding
BRB adding 418 I'm a teapot status code to all my web apps

~~~
bespoke_engnr
I actually did this in an app a few years ago, and completely forgot about it
for a while. I only remembered when another dev sent a surprised e-mail. Ya
gotta make time for fun...

------
victorbojica
It's not that shocking that this news has made it to the top here. It is very
difficult to choose the right code, tough it might sound simple...

------
myth_buster

      4XX response > Is the user being throttled? > Are you Twitter? > 420 Enhance your calm.
    

A weed reference?

------
mstade

        > Coda: On Why Status Codes Matter
        > I’m not completely sure they do matter.
        > There’s a lot of smart people at Facebook and they built an API that only ever returns 200.
    

[https://xkcd.com/927/](https://xkcd.com/927/)

~~~
buro9
The legacy of JSONP

