Skip to content
apikey.tools
SIMTesting

HTTP 401 & 403 Authentication Error Simulator

See the exact response for each auth failure mode, and what actually causes it.

SIMRuns in this tabInput not transmitted

Input

Output

computing

401 and 403 are not interchangeable, and confusing them causes real bugs in clients.

401 Unauthorized actually means *unauthenticated*. The name is a historical mistake in the HTTP specification. It means: I do not know who you are. A client should obtain or refresh a credential and may retry.

403 Forbidden means: I know who you are, and the answer is no. Retrying with the same credential will never succeed.

A client that receives 401 for a scope failure will refresh its token and retry, get 401 again, refresh again, and loop until it hits a retry limit — burning quota and producing a confusing failure.

The failure modes

ScenarioStatusUsual cause
No credential401Header omitted entirely
Malformed header401Missing scheme, or an unexpanded shell variable
Unrecognised credential401Test key sent to production — the most common cause
Expired token401Client did not refresh proactively
Insufficient scope403Token valid, permission absent
IP not allowed403Egress address not on the allowlist
Origin not allowed403Browser-restricted key from an unregistered origin
Rate limited429Budget exhausted; send Retry-After

Send WWW-Authenticate on 401

RFC 7235 requires it, and it is genuinely useful: the error and error_description parameters tell a client whether to refresh or to give up.

http
WWW-Authenticate: Bearer realm="api", error="invalid_token",
                  error_description="The access token expired"

Without it a client cannot distinguish "your token expired, refresh it" from "that key does not exist, stop trying".

Do not confirm which keys exist

Return the same response for an unknown key and a wrong key. If an unknown key gives 401 and a revoked one gives 403, an attacker can enumerate which keys were ever valid.

The same reasoning applies to error text. "Invalid credentials" for everything credential-related. Reserve specific messages for cases where the caller is already authenticated.

Test the failure paths

Every generated response comes with client test code, because authentication error handling is almost never tested and almost always subtly wrong. The two tests that matter most: a 401 triggers exactly one refresh, and a 403 triggers none.

Frequently asked questions

Should a missing API key be 401 or 403?

What about a valid key with no permission for the resource?

Should the response body say why authentication failed?

For token *format* problems — expired, malformed — yes, because the client can act on it. For "does this key exist", no. Be specific about things the legitimate caller can fix, vague about things only an attacker benefits from knowing.

Is 404 ever right instead of 403?

Sometimes deliberately. Returning 404 for a resource the caller is not permitted to see hides its existence, which matters for private repositories or documents. It is a real trade-off: it obscures information at the cost of a confusing experience for a legitimate user who has lost access.

More testing tools

Rehearse authentication before you ship it