41

Over the years, I have seen quite a few questions on this site along the lines of "can I invent my own HTTP response codes"? Generally asked by those who are developing both the server and client. The responses tend to go towards sticking with standard codes.

If I stick with standard HTTP status code numbers, is there any technical reason not to use custom text in order to differentiate between, let's say, multiple 501 responses?

To reiterate, no client except mine will ever see these values, which are returned by AJAX in repose to authenticated requests.

14
  • 10
    No I don't see any technical reasons why not. But I'd probably prefer such to lift it to a higher protocol level than transport from an architectural POV. I.e, not using 501 at all. Jan 25, 2020 at 10:03
  • 25
    Any chance of proxies/firewalls in between? Could be an issue if you use "non-standard" stuff.
    – Mat
    Jan 25, 2020 at 10:55
  • 4
    Handle such with the Ajax payload, that's what I meant. Jan 25, 2020 at 11:25
  • 11
    What is the actual problem you want to solve? Jan 25, 2020 at 23:55
  • 7
    The approach at every place I've worked at in the past decade has been to use a standard content type and message body in the error response. Usually that's JSON, but sometimes it's text/plain, XML, or even text/csv. You can even do binary with protobuf or capnproto or parquet.
    – ngreen
    Jan 27, 2020 at 17:32

4 Answers 4

43

An alternative approach would be to include a response body containing the detailed failure reason, for example in a JSON object. This would be comparable to the customized 404 pages which are often delivered to browsers when a non-existent web page is opened.

For example, if your backend is written in Python using Django, you could implement a middleware function (see https://docs.djangoproject.com/en/3.0/topics/http/middleware/) to catch exceptions and return a 500 error with some error JSON object (note this is completely untested, just use it to get an idea how it could be done):

import sys, traceback
from django.http import JsonResponse

def exception_handling_middleware(get_response):

    def middleware(request):

        try:
            # call inner handler
            return get_response(request)
        except BaseException:
            # if any exception happens, return an error response
            # with a 500 status code
            return JsonResponse(
              {
                "error": str(sys.exc_info()[1]),
                "traceback": traceback.format_exc()
              },
              status=500)
2
  • 24
    Note: sending tracebacks to clients could potentially leak sensitive information.
    – user253751
    Jan 27, 2020 at 8:51
  • 13
    @user253751 yes, of course you would only do that in a development/acceptance environment, thank you for pointing this out! Jan 27, 2020 at 8:53
86

The “status text” doesn't exist anymore in HTTP/2. There is only the numeric code. So you won't be able to use HTTP/2 (or later versions).

According to section 8.1.2.4 Response Pseudo-Header Fields of RFC 7540, which specifies HTTP/2:

HTTP/2 does not define a way to carry the version or reason phrase that is included in an HTTP/1.1 status line.

2
  • 1
    This should be the accepted answer.
    – Ian Kemp
    Jan 28, 2020 at 9:14
  • 2
    HTTP/2 does not obsolete HTTP/1.1. This is clearly said in the HTTP/2 RFC, that the binary message protocol defined by HTTP/2 is intended as an alternative and not a replacement of HTTP/1.1.
    – Lie Ryan
    Jan 28, 2020 at 11:38
50

While you may control all the clients and servers, you also has a third end you need to be aware of: the intermediates.

The intermediates are web servers, proxies, caches, web application firewall, load balancer, CDN, and other intermediate systems that processes the HTTP message that may stand between your clients and servers. Even if you use end to end encryption and control all the intermediaries used, it's generally easier to integrate new intermediaries into the system when you stick to the standards.

With that said, unless configured otherwise, standard compliant intermediaries normally should only use status code when deciding how to process the message and should have ignored the reason phrase, so in most cases it should be safe to customise the reason phrase. The reason phrase should be reserved to only carry human readable reason that shouldn't affect how the message should be processed.

Some references from RFC 7231 (Section 6.1) (emphasis mine):

The status codes listed below are defined in this specification, Section 4 of [RFC7232], Section 4 of [RFC7233], and Section 3 of [RFC7235]. The reason phrases listed here are only recommendations -- they can be replaced by local equivalents without affecting the protocol.

And from RFC 7230 (Section 3.1.2) (emphasis mine):

The reason-phrase element exists for the sole purpose of providing a textual description associated with the numeric status code, mostly out of deference to earlier Internet application protocols that were more frequently used with interactive text clients. A client SHOULD ignore the reason-phrase content.


So what should you do instead if standard HTTP status codes aren't sufficient for you?

Use the status code. HTTP Status code is designed to be extensible. Refer to RFC 7231 Section 6 on how to extend HTTP status code in a way that would remain backwards compatible with clients and intermediaries that doesn't understand the extended status code.

-12

By all means use your own codes as the standard HTTP codes really are not that good, but my advice is to keep them separate for maintenance sake and not to confuse anyone who one day may use your code, so rather than 3 digit codes with a decimal point, go with say 5 digit codes

10
  • 2
    In your answer you state some things like "the standard HTTP codes really are not that good", and that 5-digit codes should be used. Can you provide arguments or sources for these statements? That would make for a better answer.
    – Tvde1
    Jan 27, 2020 at 11:31
  • 8
    Using 5 digits code would break the HTTP standard and any middleware handling the message (proxy, firewall, load-balancer, ESB, CDN, monitor, etc.). If you really need to use custom code, you should stick to 3 digit and respect the first digit meaning (2xx = success, 4xx = client error, 5xx = server error etc.). If you don't do that, your protocol is no longer HTTP compliant and you better be able to justify the need of a custom protocol.
    – zakinster
    Jan 27, 2020 at 11:38
  • 1
    There's rarely a reason to create a new status code instead of using an existing one and providing additional information via a response body or header. But if you do, follow the standard (but again, rarely the right thing).
    – Voo
    Jan 27, 2020 at 12:54
  • 1
    This appears to be answering a different question. The question on this page clearly states that they intend to use standard HTTP status code numbers, but vary the textual phrase next to them.
    – IMSoP
    Jan 28, 2020 at 11:06
  • 2
    I don't know exactly your situation in detail, of course, but I'd say that the case of the remote server returning 404 is irrelevant to the client of your API. How your API does whatever it does is none of the my business, so if the remote server 404's, I don't care - I only care about what I asked your API to do, and as far as I'm concerned if your API fails to do it then that's your API failing, not the remote one. If your API can carry on and do its job despite the remote 404, return a 200 and nobody but you needs to know; if it can't, 5xx and maybe some detail in the body. Jan 28, 2020 at 16:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.