Quick Start: Learn five mental models that cover every HTTP response, then read live codes with curl. Total time: 20 minutes.

What You’ll Learn

  • What an HTTP status code is, and what problem it solves.
  • Five mental models that make any code readable, not just the ones you’ve memorized.
  • The mnemonic for all five classes, and four more models for the hard cases.
  • The handful of codes you’ll meet 95% of the time.
  • When the code lies to you (200 with an error inside).
  • How to read status codes yourself with curl.
  • Where to go to learn the long tail.

The Basics

Every HTTP response carries a three-digit status code: the server’s verdict on your request, delivered before any body content. The first digit classifies responses into five categories; knowing those five is enough to read any code you encounter.

The registry has over 60 codes, but you can ignore most of them. The five mental models below cover the first digit, the common failure cases, and the specific codes you’ll see repeatedly.

Primary Use Cases

  • Debugging a failing API call by reading the response, not guessing.
  • Writing a server that returns honest, correct codes to its clients.
  • Reading logs and monitoring dashboards where codes are the signal.

Less Suitable Use Cases

  • Status codes describe what happened, not why. For the why, read the response body and headers.
  • They are not a security boundary. A 403 tells an attacker the resource exists; sometimes 404 is the safer answer.

Mental Models for HTTP Status Codes

A mnemonic helps you memorize the five classes. A mental model helps you reason about a code you haven’t seen before. These five models work together: the first gets you to the right category, the next three handle the most common confusions, and the last one keeps you honest when a 2xx shows up.

Mental Model 1: The Five-Class Mnemonic

The first digit groups every response into five buckets.

  1. 1xx Hold on: the server got your request and is still working.
  2. 2xx Here you go: success, here’s what you asked for.
  3. 3xx Go away: what you want lives somewhere else; go look there.
  4. 4xx You messed up: the request was wrong; fix it and retry.
  5. 5xx I messed up: the request was fine, the server broke.

The first digit alone narrows 60-plus codes to one of five categories. Know these five phrases, and you can orient any response before looking up a specific code.

graph TB R[Three-digit status code] --> A[1xx Hold on] R --> B[2xx Here you go] R --> C[3xx Go away] R --> D[4xx You messed up] R --> E[5xx I messed up]

Mental Model 2: The Fault Line

When something goes wrong, the first question is: whose bug is it? The 4xx/5xx split answers that in one digit.

4xx means the request was wrong. The caller sent something the server couldn’t use: a missing header, malformed JSON, a nonexistent resource, or a request without credentials. The server did its job by refusing.

5xx means the server broke. The request was fine, but the server-side failed: an unhandled exception, a crashed database, or an overloaded process. This is the server’s problem, not the caller’s.

That single distinction settles most “whose bug is it?” arguments before they start. One caveat: 4xx is the server’s opinion that you messed up. The server can be wrong.

graph TB E[Something went wrong] --> F{First digit?} F -->|4xx| G[Client sent a bad request] F -->|5xx| H[Server broke] G --> I[Fix the request] H --> J[Wait, retry, or file a bug]

Mental Model 3: The Identity vs Permission Distinction

401 and 403 both deny access. They signal completely different problems, and mixing them up wastes debugging time.

401 Unauthorized is an identity question. The server doesn’t know who you are. You haven’t authenticated, or your session token has expired. The right move: log in or refresh credentials.

403 Forbidden is a permission question. The server knows exactly who you are and still refuses. Your credentials are valid, but your account lacks the right roles or scopes. The right move: check permissions, not your login.

Think of 401 as the server asking “Who are you?” and 403 as the server saying “I know who you are, and no.”

graph TB A[Access denied] --> B{Which code?} B -->|401 Unauthorized| C[Server does not know who you are] B -->|403 Forbidden| D[Server knows you and denies access] C --> E[Fix: authenticate or refresh token] D --> F[Fix: check roles and scopes]

Mental Model 4: The Redirect Chain

3xx codes are forwarding addresses. The resource you requested has moved, and the server is pointing you to its current location.

301 Moved Permanently means you need to update your stored URL. The resource has a permanent new address. Clients, search engines, and caches should all update.

302 Found (temporary) means keep your original address. The resource is elsewhere for now but may return. Don’t update your stored URL yet.

304 Not Modified is different from both. It isn’t a redirect at all: the cached version is up to date, and there’s nothing new to download. This saves bandwidth on every repeated request.

Browsers follow 3xx redirects automatically. Scripts and CLI tools usually need an explicit flag. In curl, that’s -L.

graph TB T[3xx response] --> A{Which code?} A -->|301| B[Permanent: update stored URL] A -->|302| C[Temporary: keep original URL] A -->|304| D[Cache valid: skip the download]

Mental Model 5: The Two-Layer Trust

The HTTP status code is the server’s public verdict. The response body is the full story. Either can mislead you.

The classic failure: an API returns 200 OK with an error buried in the JSON body.

{ "status": 200, "body": { "error": "internal server error" } }

GraphQL uses this by design, returning 200 for every request and placing real errors in an errors array. Some REST APIs do it by accident when an unhandled exception gets wrapped in a 200 response. The HTTP layer says success while the application layer disagrees.

The rule: check the status code first, then read the body before you assume anything worked. The code is the door; the body is the room.

The Codes You Actually Meet

Out of 60-plus codes, these carry almost all daily traffic. Learn these and look up the rest.

  • 200 OK: the standard success. The request worked, and the body has your data.
  • 201 Created: success, and a new resource now exists (a POST that saved something).
  • 204 No Content: success, but there’s nothing to return (a DELETE that worked).
  • 301 / 302 Moved: redirect. 301 is permanent; 302 is temporary. Your browser follows these automatically.
  • 304 Not Modified: your cached copy is still good; save the bandwidth.
  • 400 Bad Request: the server couldn’t parse what you sent. Malformed JSON, missing fields.
  • 401 Unauthorized: “Who are you?” You aren’t authenticated. Log in.
  • 403 Forbidden: “Who do you think you are?!” You’re authenticated, but you can’t have this.
  • 404 Not Found: no resource at that URL. Could be your typo, could be a bad link the server published.
  • 429 Too Many Requests: slow down, you’re rate-limited. Back off and retry later.
  • 500 Internal Server Error: the server threw an unhandled exception. Not your fault.
  • 503 Service Unavailable: the server is up but overloaded or down for maintenance.

The Famous Joke Code: 418

If you ever see 418 I’m a teapot, it’s real, sort of. It comes from RFC 2324, the Hyper Text Coffee Pot Control Protocol, an April Fools’ joke from 1998. A teapot asked to brew coffee returns 418. Nobody expected it to matter. Then smart kitchen devices arrived; a networked teapot can technically return it and remain compliant. Most frameworks still ship it. It’s one of the developer community’s favorite inside jokes.

Build Something: Read Status Codes With curl

You don’t need a teapot to see status codes. curl shows you the real thing in seconds.

Prerequisites

Required:

  • curl (preinstalled on macOS and most Linux; available on Windows).
  • A terminal.
  • About 5 minutes.

Not required:

  • Any account or API key. These hit public test endpoints.

Step 1: See the status line

Use -I to fetch just the response headers, including the status line.

curl -I https://httpbin.org/status/200

Expected output:

HTTP/2 200
date: ...
content-type: text/html; charset=utf-8

The 200 on the first line is the verdict. That’s the whole game.

Step 2: Trigger each class on demand

httpbin.org/status/<code> returns whatever code you ask for. Try one from each class:

curl -o /dev/null -s -w "%{http_code}\n" https://httpbin.org/status/301
curl -o /dev/null -s -w "%{http_code}\n" https://httpbin.org/status/404
curl -o /dev/null -s -w "%{http_code}\n" https://httpbin.org/status/503

Expected output:

301
404
503

Here, -w "%{http_code}\n" prints only the status code, and -o /dev/null -s discards the body and progress meter. This one-liner works for scripting health checks.

Step 3: Watch a redirect resolve

By default, curl ignores redirects. Add -L to follow the 3xx chain to its destination.

curl -I -L "https://httpbin.org/redirect-to?url=https://example.com"

Expected output:

HTTP/2 302
location: https://example.com
HTTP/2 200
content-type: text/html; charset=utf-8

The first response is the 302 redirect, the second is the 200 from the destination. curl walked the chain for you.

What you learned: You can now read any response’s verdict, force any code for testing, and trace a redirect. Those three moves cover most real HTTP debugging.

Troubleshooting

“I get a 200, but the app still shows an error.”

The server returned success at the HTTP layer but failed at the application layer. Read the response body. This happens with GraphQL and with APIs that wrap exceptions in 200 responses.

“I’m authenticated but still getting 403.”

403 is about permission, not identity. Your credentials are valid, but your account lacks access to that specific resource. Check roles and scopes, not your login.

“My script’s curl returns nothing for a 500.”

A 5xx often comes with an empty or HTML body. Print the status code directly with -w "%{http_code}\n" rather than relying on body content.

Learn HTTP Status Codes - Beyond the Basics

You’ve covered the essentials. Here’s a curated track to go deeper. Start with the official spec for authority, then keep a visual cheat sheet handy for the codes you forget.

📹 Video

🔊 Audio

  • HTTP 203 - Jake Archibald and Surma’s web platform podcast, named after a status code and full of HTTP deep dives.

📚 Books

🌐 Online


Related Articles by Category

Learn X

Begin learning new software frameworks, languages, tools, and techniques then leave with resources for further study.