ByteScope

401

How to fix HTTP 401 Unauthorized

4xx client errorIETF standardRFC 9110

The request carried no usable authentication credentials — the reason phrase says Unauthorized, but the condition it describes is unauthenticated, and the header naming what would work is mandatory.

Why you are seeing 401, and what to do about it

The same three digits are three different problems depending on which side of the request you are on. Read the block that describes you.

Reproduce 401 with curl

Run this against the URL that failed. It prints the status without the error page, so you can see what the server said rather than what the browser rendered.

curl
curl -sS -o /dev/null -D - -w '\n%{http_code}\n' https://example.com/api/me

`-sS` drops the progress meter while keeping error messages, `-o /dev/null` throws away the error page body, `-D -` dumps the response headers to stdout, and `-w` prints the status on its own line at the end. The header you came for is `www-authenticate`: §15.5.2 makes it mandatory on a 401, its first token is the scheme, and on a Bearer API the `error` parameter separates a missing token from an invalid one from an expired one. If that header is absent, the server is not answering to spec and its own logs are now your only source. Then run the command twice more to split the two cases the status code cannot: once with `-H 'authorization: Bearer <token>'` and once with `-u wrong:creds`. A 401 for both means the validator is refusing everything, which points at the key, the issuer or the audience rather than at your token; a 401 only when you send the real token points at that token being expired or revoked. Note that `-u` makes curl send Basic credentials preemptively, so a 401 in that run says nothing about whether the server ever offered Basic — only the `www-authenticate` header does. If the challenge names a proxy rather than the site, look at 407 instead.

What 401 actually means

RFC 9110 §15.5.2 defines 401 as the request not having been applied because it lacks valid authentication credentials for the target resource, and it attaches a hard requirement: a server generating a 401 MUST send a `WWW-Authenticate` header field carrying at least one challenge. That header is the whole difference between a 401 and a 403 — it exists because retrying with different credentials could plausibly succeed, so the server owes the client a machine-readable statement of which credentials. A 401 with no challenge is a spec violation, and it is also the version of this code that wastes the most of your afternoon, because it removes the only field that says what the server wanted. The same section covers the case people find surprising: if the request did include credentials, the 401 means authorization has been refused for those credentials. So one status code covers both “you sent nothing” and “you sent something and it was rejected”, and nothing in the status line separates them — which is why your own logs have to. §15.5.2 also tells a user agent that repeats the request and gets back the same challenge to show the response body to the user, since it usually carries the diagnostic detail. Two more facts change where you look. The challenge grammar in §11.3 is a scheme name followed by parameters, of which `realm` names the protection space, and the scheme is what decides browser behaviour: a `Basic` challenge (RFC 7617) makes the browser render its own username and password dialog on top of your application. And for token APIs, RFC 6750 §3.1 splits the cases by status rather than leaving them to taste — `invalid_request` is a 400, `invalid_token` is a 401, and `insufficient_scope` is a 403. Read that way, a 401 from an OAuth-protected API says the token was missing, malformed, expired or revoked, while a token that is genuinely yours but too narrow should have come back as 403. Finally, 401 is not among the status codes RFC 9110 §15.1 marks heuristically cacheable, so no proxy will cache one on its own initiative.

Codes 401 gets mistaken for

Confused withHow to tell them apart
403401 says the server does not know who you are; 403 says it knows and the answer is still no. The reliable test is not the wording but the header: RFC 9110 requires `WWW-Authenticate` on a 401 because retrying with different credentials could work, and requires nothing of the sort on a 403 because it could not. RFC 6750 §3.1 draws the same line for OAuth — a token that is missing or invalid is `invalid_token` and a 401, while a valid token lacking the necessary scope is `insufficient_scope` and a 403.
404A resource you are certain exists can answer 404 to an unauthenticated caller quite legitimately: §15.5.4 permits a server that does not want to admit a forbidden resource exists to send 404 instead, and private repositories and object storage do exactly that. So before you debug a 404 as a routing bug, retry it with credentials — if it turns into a 200 or a 403, it was an access decision in disguise all along.
400An `Authorization` header the server cannot parse at all is a malformed request rather than a rejected identity, and RFC 6750 §3.1 says so explicitly: `invalid_request` should be answered with 400, not 401. Frameworks disagree in practice, so a 400 on an authenticated endpoint is worth reading as “your header is the wrong shape” — a missing `Bearer ` prefix, a stray newline, or two `Authorization` headers — before you assume the token itself was refused.
407407 (Proxy Authentication Required, §15.5.8) is the same idea one hop earlier: a proxy, not the origin, wants credentials, and it challenges with `Proxy-Authenticate` rather than `WWW-Authenticate`. On a corporate network a 401 you cannot explain is often a 407 you did not read carefully, and the giveaway is that the challenge arrives for every host you try rather than only for the API you are working on.
BasicThe Basic scheme (RFC 7617) encodes `user:password` in base64, which is an encoding rather than encryption — anyone who can see the header can read the password, so it is only ever safe over TLS. It also has a user-interface consequence people rarely intend: a browser that receives a `Basic` challenge renders its own credential dialog over the page, which is why an API meant to be called from JavaScript should challenge with `Bearer` instead.
Reason phrase
Unauthorized
Class
4xx client error
Defined by
RFC 9110
Standard
IETF standard

Tools on this site

All of these run in your browser — nothing is uploaded.

Related status codes

Codes people usually end up reading in the same session as 401.

Frequently asked questions

How do I find out which server produced this status code?

Read the response headers rather than the page. curl -sS -o /dev/null -D - https://example.com/path prints them without the body, and server, via and cf-ray between them name the layer that answered. A cf-ray value means Cloudflare handled the response and is the id their support will ask for. To take the edge out of the picture entirely, repeat the request with --resolve example.com:443:203.0.113.10, which connects to the origin address you name while still sending the original host name and SNI — if the answer changes, the edge and the origin disagree.

Does the reason phrase after the number matter?

No, and you should never branch on it. RFC 9112 tells clients to ignore the reason phrase, servers are free to change it, and HTTP/2 and HTTP/3 do not carry one at all — so 404 Not Found over HTTP/1.1 arrives as a bare 404 over HTTP/2. The phrases on this site are the registered ones because they are what people search for and what appears in an HTTP/1.1 log, not because any software depends on them.

Should I retry a request that failed with this code?

Retry 5xx and 429; do not retry 4xx, because nothing about the request will be different next time. If the response carries Retry-After, honour it — RFC 9110 defines it for exactly this, and it may be either a number of seconds or a date. Otherwise use exponential backoff with jitter and a hard cap, and only for idempotent methods: a retried POST can charge a card twice. A retry storm against a server that is already failing is how a brief incident becomes a long one.

Can I make my server return a different status code?

Yes, and the only rule that matters is that the code has to be true. Anything that reads your responses automatically — search engines, monitoring, caches, client retry logic — makes decisions from the number and never from the page. Serving an error page with 200 hides the failure from your own alerting; serving 200 for a missing page gets the error indexed as content; returning 500 for a request that was simply malformed sends whoever is on call to the wrong half of the stack.

Still stuck on 401? browse every status code in the reference — or go back up to the block written for your side of the stack.