401 vs 403 (and the gateway trio), finally explained

Two status codes cause more pointless re-logins than any other, and three more cause more pointless page-refreshes. The first pair — 401 and 403 — get mixed up because they're neighbors. The second trio — 502, 503, 504 — get mixed up because they all mean "the server is sad."

Here's the thing: each of these five codes answers a specific question, and once you know which question, you stop guessing. This is the post you bookmark so you never type "why is this a 403" into a search bar again.

In this post
  • 401 vs 403 in one sentence each — and why re-logging in fixes only one
  • The gateway trio: 502 vs 503 vs 504, and what each one actually means
  • A who-to-blame table you can screenshot
  • What to actually do for each of the five codes

First, the two halves of HTTP errors

Status codes split into two halves that tell you where to look:

4xx — the problem is in your request. The client side. Something about what you sent — or who sent it — doesn't work for the server.

5xx — the problem is in their infrastructure. The server side. Your request was fine; something behind the thing you talked to failed.

One nuance before we go further: "client side" does not mean "your fault." A 401 can be caused by the server's clock being wrong. A 403 can be a misconfigured permission. The 4xx/5xx split tells you which end to start investigating — not who to blame. There's a table for blame later.

401 — "who are you?"

401 Unauthorized has the most misleading name in the protocol. It's not about authorization at all. It's about authentication — the server is saying:

"I don't know who you are. You sent no credentials, or credentials I don't recognize."

Per RFC 9110, a 401 means the request "lacks valid authentication credentials," and the server must tell you how to authenticate via a WWW-Authenticate header. The honest name would be "401 Unauthenticated."

In practice, a 401 means one of:

  • No credentials at all — the Authorization header never made it onto the request.
  • Expired credentials — the token's exp claim is in the past. The most common cause by far.
  • Malformed credentialsBearer misspelled, extra whitespace, quotes around the token.
  • Wrong environment's credentials — a staging token against the production API.
  • Clock skew — the token is fine, but the server thinks it's later than it actually is.

The move: fix the credentials and resend. A 401 is almost always solvable by authenticating correctly. If you've got a token in hand, decode it first — half the time the exp claim tells you the whole story.

403 — "I know who you are. No."

403 Forbidden is the opposite problem. Here the server is saying:

"I know exactly who you are. And the answer is no."

Authentication succeeded — your credentials are valid — but authorization failed. You don't have permission for this thing. Per the RFC, the server "understands the request but refuses to authorize it."

The single most useful fact in this post: re-authenticating never fixes a 403. If logging out and back in changes the outcome, it wasn't really a 403-shaped problem. A 403 is a permissions problem, and the fix is on the account side, not the login side.

Common causes:

  • Missing role or scope — you're authenticated as a user; the endpoint wants an admin.
  • Someone else's resource — you're asking for user 42's data while authenticated as user 41. (This 403 is the system working correctly.)
  • IP allowlist or WAF rule — your credentials are fine; your address isn't welcome.
  • Server-side file permissions — the web server can't read what it's being asked to serve.

The move: figure out what permission is missing and request it. Do not log in again. It won't help, and you'll be late to the meeting.

The re-auth test

Log out, log in, retry. If it's fixed, the original problem was authentication — 401-flavored, even if the code said otherwise. If nothing changed, it's authorization. Stop touching your credentials and go look at permissions.

The auth decision, in one table

The whole argument on one card:

auth-codes.txt
                 401 Unauthorized           403 Forbidden
───────────────────────────────────────────────────────────────
question         "who are you?"             "I know who you are. no."
problem          authentication             authorization
server says      "no valid credentials"     "credentials fine — refused"
re-login fixes   usually                    never
your move        fix + resend credentials   fix the permission, not the login

Tape it next to the monitor. The row that saves you the most time is the fourth one.

The gateway trio — never your browser's fault

Now the other side of the street. 502, 503 and 504 are all 5xx, which means the first thing to internalize: your request was fine. These are not your bug. They're a report from somewhere in the middle of someone's infrastructure.

To see why, picture the chain your request actually travels:

the-chain.txt
your browser  CDN  load balancer  nginx  app server
                                                  
        the gateway trio is what these middle layers say
        when the thing behind them misbehaves

Every one of those arrows is a "gateway" — a thing that forwards your request to the next thing. The trio is what a middle layer says when the thing behind it misbehaves. The differences are all about how it misbehaved.

502 Bad Gateway — the upstream is dead or lying

A 502 means the gateway forwarded your request and got back an invalid response — or, in practice, got nothing coherent at all. The upstream is down, crashed, not listening on the port, or returning garbage.

The classic setup: nginx in front of an app server (php-fpm, Node, Gunicorn) that has died. nginx is healthy, so it answers — but all it can say is "the thing behind me is broken."

If it's your server

A 502 on your own infrastructure almost always means your application process is not running. Check it. Restart it. Then set up the monitoring that would have told you.

As a user, a 502 is an outage, not a bug in your request. Retry once. If it persists, check the status page — this one is a waiting game.

503 Service Unavailable — the polite one

A 503 is the well-behaved 5xx. The server is saying: "I'm here, I understand you, and I cannot help you right now — I'm overloaded or in maintenance."

Crucially, a 503 is often intentional. A service under load sheds traffic on purpose rather than falling over, and maintenance mode returns a 503 by design. A well-behaved one includes a Retry-After header telling you when to come back.

This is also the code you should return when your own service is overloaded — not a 500, and definitely not a hang. A 503 with Retry-After is a server communicating. A 500 is a server having an accident.

As a user: respect Retry-After, back off, retry. This one usually fixes itself.

504 Gateway Timeout — slow, not dead

A 504 means the gateway forwarded your request, waited, and gave up. The upstream didn't send a bad response — it sent no response in time. It's alive, but it's too slow.

That's the whole distinction from 502, and it's a useful one:

  • 502 — the upstream answered badly (or is dead and can't answer).
  • 504 — the upstream is alive but didn't answer in time.

A 504 usually means a slow query, a hung worker, or a job that grew past the timeout. As a user, retrying can genuinely help — the slow thing may have cleared. As an owner, a 504 is a performance bug wearing a costume: go find the slow query.

The who-to-blame table

All five on one card:

who-to-blame.txt
code   name                   what it means               blame           do this
────────────────────────────────────────────────────────────────────────────────
401    Unauthorized           no/invalid credentials      your auth     fix + resend credentials
403    Forbidden              authed but not allowed      your perms    ask for access, don't retry
502    Bad Gateway            upstream dead or broken     their infra   retry once; check status page
503    Service Unavailable    overloaded / maintenance    their infra   respect Retry-After, back off
504    Gateway Timeout        upstream too slow           their infra   retry; go find the slow thing

Screenshot it. The "blame" column is the point — it tells you whether to fix your code or open a status page.

How to actually debug these

Codes tell you where to look; they don't tell you the answer. Here's the actual workflow:

  • Read the response body. The status line is a category; the body usually has the real error. 403 Forbidden: requires scope admin:write is a complete diagnosis.
  • For a 401, decode the token. Nine times out of ten the exp claim or a wrong issuer is staring at you. The JWT decoder does it in one paste — and the JWT post has the full checklist.
  • For a 403, stop retrying. Figure out what permission is missing. Retrying an authorization failure is just noise.
  • For the trio, ask: all requests, or mine? If everything is 502, it's an outage — status page, not code. If it's just yours, your request may be routing somewhere broken.
  • Retry 503 and 504 with backoff. Never hammer.
Don't hammer auth errors

A tight retry loop on a 401 or 403 can trip rate limits or lock the account — turning a small problem into a big one. Auth errors are for reading, not for retrying.

And when you hit a code that isn't one of these five, the HTTP status lookup has all 71 — with a who's-responsible tag on every one.

Wrap-up

Five codes, five questions. 401: who are you? 403: who are you — and no. 502: the thing behind me is dead. 503: I'm busy, come back later. 504: the thing behind me is slow.

Learn those and most "server errors" stop being mysterious and start being a to-do list with an owner.

Next up: the Conventional Commits cheat sheet — because the only thing worse than debugging a 502 is reading the commit that caused it.