← Glossary

Protocols & Acronyms

HTTP Status Codes (a security reading)

Also: HTTP status code · HTTP response code · 401 · 403 · 429 · HTTP error codes

Last reviewed:

In one sentence

HTTP status codes are the three-digit numbers a server returns with every response (200 OK, 404 Not Found, 500 Server Error), and read carefully they leak security-relevant information — whether an account exists, whether you are rate-limited, or whether the server just spilled a stack trace.

Why it matters (the security angle)

Everyone knows 404. The reason status codes earn a place in a security glossary is that the difference between two codes is often an information leak or a control. This page is deliberately not a full HTTP reference — it is the security reading of the codes a pentester, a SOC analyst and an API defender actually reason about.

The classic example is authentication. A login that returns 401 Unauthorized for "wrong password" but 403 Forbidden or a different code for "no such user" hands an attacker a username oracle: they can enumerate valid accounts by watching which code comes back, without ever guessing a password. The same logic runs through status codes everywhere — they are a side channel, and reading them precisely is a real skill.

The classes, and what each leaks

Codes group into five classes; here is the security-relevant behaviour of each:

  • 3xx (redirects) — a 302/307 whose target comes from user input is the open-redirect bug: ?next=https://evil.example turns your trusted domain into a phishing springboard, and it is a common step in OAuth token theft. Redirects are also where a downgrade to http:// can be slipped in — the reason HSTS exists.
  • 401 vs 403 — 401 means "not authenticated" (who are you?); 403 means "authenticated but not allowed" (I know who you are, no). Mixing them up, or letting the pair distinguish "wrong password" from "unknown user", leaks account existence.
  • 429 Too Many Requests — not really an error but a control: it is how rate limiting answers brute-force and credential-stuffing. Its absence under a flood of failed logins is itself a finding.
  • 5xx (server errors) — a 500 that returns a stack trace, SQL error or framework version is information disclosure; production should return a generic body and log the detail server-side.

Two more habits: verbose codes aid enumeration (a 200 vs 404 difference maps out which files or IDs exist), and any code can be forged by an intermediary — trust the code plus the body plus the headers together, never the number alone.

Try it yourself hands-on

Look codes up for their meaning, then probe how a real endpoint uses them.

  1. Open the HTTP Status Codes reference and filter to 4xx Client Error. Compare 401 ("authentication required") with 403 ("server refuses to authorize") and note 429 is described as rate limiting / DDoS protection — the security framing above, in one glance. Search 3xx and read the redirect codes with the open-redirect trap in mind.
  2. Now test behaviour, not just definitions: with the HTTP Request Builder, send a request to your own endpoint with a bad password, then with a non-existent user, and compare the status codes. If they differ, you have found a username oracle — a real finding for the report.
  3. Hammer a login endpoint (one you own) a few times and check whether a 429 ever appears. No 429 under repeated failures means no rate limiting — a brute-force gap worth flagging.
  4. Round out the response-security picture with the HTTP Security Headers Grader: the same responses should also carry the right security headers (HSTS, CSP), so you fix the status-code and header story together.

Result: you read status codes as a security signal — auth enumeration, missing rate limiting, open redirects, leaky 500s — instead of just "the site returned an error".

Common misreadings

  • Don't let 401/403 leak existence. For unauthenticated flows (login, password reset), return the same response for "wrong password" and "unknown user" so the code cannot enumerate accounts.
  • A 200 is not "safe". Attacks succeed with 200s all the time; the body matters as much as the code. Conversely a 403 does not mean the resource is truly protected — test, don't assume.
  • Never redirect to an unvalidated target. Allow-list redirect destinations; a user-controlled 3xx target is an open redirect and a phishing/OAuth-theft vector.