The Problem: Why Are There So Many HTTP status codes and Why Are they so confusing?!

I once set out to build a simple REST API. All I had to do was return a response with the right HTTP status code. Simple, right? Wrong.

My first instinct was to follow the "success or failure" approach. If things worked, I’d return 200 OK. If things didn’t work, I’d return 400 Bad Request. Done.

Then, reality slapped me in the face. A senior engineer came along and asked:

"Why are you returning 400 when it’s clearly a 422 Unprocessable Entity? And why are you not using 409 Conflict when there’s a duplicate entry? Oh, and don’t forget 304 Not Modified for caching. Also, rate limiting? 429 Too Many Requests."

And I just stood there, staring at my screen, questioning my life choices.

Examples of the Madness

Here’s a real-world example of how confusing HTTP status codes can be:

  • 500 Internal Server Error: Did my backend crash? Is my database down? Or did my load balancer just decide that today was not the day?
  • 404 Not Found: Is the endpoint missing, or is my routing broken? Or did I just typo my URL?
  • 401 Unauthorized vs. 403 Forbidden: Ah, yes. So I’m not authorized? But wait, am I not authorized because I’m missing credentials (401)? Or do I have credentials, but I’m just not worthy (403)?
  • 429 Too Many Requests: Ah, I see. My API is punishing me for being too enthusiastic.

Why Are These Status Codes Even a Thing?

At this point, you might be asking, "Why do we even need all these?" I asked the same thing. Wouldn’t it be easier if we just had:

✅ 200 OK for success❌ 400 Bad Request for failure

Done. Simple. Clean. No headaches.

But then reality struck again. HTTP status codes exist to help machines make decisions quickly.

The Secret Behind Why This Works

  1. Browsers need caching (304 Not Modified) so they don’t waste bandwidth downloading the same thing over and over.
  2. Load balancers need to know (502 Bad Gateway) when a backend is dead so they can reroute traffic.
  3. APIs need rate limiting (429 Too Many Requests) so users don’t take down the entire system.
  4. Search engines need redirects (301 Moved Permanently) so they know where to send users.
  5. Clients need to know when to retry (503 Service Unavailable means "try again later").

By forcing clients to check the status code first instead of parsing the response body, we make APIs, browsers, and entire distributed systems faster and more efficient.

The Minimal Set of HTTP Status Codes You Actually Need

If we trim the fat, here’s what really matters:

🙃 Instruction Informational Codes:

  • 100 Continue - Server has started getting your data of the request, continue to do so.
  • 101 Client is requesting protocol switch.

✅ Success Codes:

  • 200 OK - Everything’s fine.
  • 201 Created - You made something new.
  • 204 No Content - Success, but there’s nothing to return.

📢 Redirection Codes:

  • 301 Moved Permanently - This link changed forever.
  • 304 Not Modified - Use your cache, dude.

🚨 Client Error Codes:

  • 400 Bad Request - Your request is broken.
  • 401 Unauthorized - Log in first.
  • 403 Forbidden - You’re logged in, but still not allowed.
  • 404 Not Found - It doesn’t exist.
  • 429 Too Many Requests - Slow down!

💥 Server Error Codes:

  • 500 Internal Server Error - The backend is on fire.
  • 502 Bad Gateway - The backend is dead.
  • 503 Service Unavailable - The backend is taking a break.
  • 504 Gateway Timeout - The backend is slow.

Final Thoughts: Do We Still Hate Status Codes?

Okay, so HTTP status codes are confusing, but they exist for a reason. They keep the internet running efficiently.

Would it be simpler to just return 200 for success and 400 for failure? Maybe. But then browsers, load balancers, CDNs, and search engines would break.

So while I still hate memorizing them, I have to admit—they’re useful. Now, if you’ll excuse me, I need to go debug another 500 error that my API just threw.