Authorization

This page is about the header itself — its grammar, the schemes that can appear in it, the challenge that asks for it, and the rules that decide when a client will silently refuse to send it. Which credential you should be using is a separate question, answered in the guides to API authentication and bearer tokens. What follows is the mechanics, because a 401 caused by a malformed header looks exactly like a 401 caused by a wrong password, and the two have nothing in common.

The grammar

RFC 9110 defines the field as an authentication scheme followed by credentials, separated by a single space:

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.dBjftJeZ4CVP…
               ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               scheme credentials

The scheme name is a token, and it is case-insensitiveBearer, bearer and BEARER are the same scheme, though matching them all is the server's job and not every server does it. The credentials that follow are opaque to HTTP itself: the protocol defines the envelope and each scheme defines what may go inside it.

There are exactly two shapes credentials may take, and knowing which shape a scheme uses explains most of the syntax you will ever meet.

Form 1: token68

A single opaque blob, from a deliberately restricted alphabet: letters, digits, and - . _ ~ + /, with = permitted only as trailing padding. The name refers to those 68 allowed characters. The set exists because it covers base64, base64url and hex without needing any quoting or escaping, so a credential can be dropped into the header untouched.

Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
Authorization: Bearer 6f8d9c2b-1e4a-4f77-9a03-8f1e2c7b4d55

Notice what the alphabet excludes: spaces, commas, quotes and newlines. A token containing any of those is not a valid token68, which is why a credential pasted from a wrapped terminal window — carrying an embedded newline — produces a header the server cannot parse and a 401 that says nothing useful.

Form 2: a list of auth-params

Alternatively, credentials may be a comma-separated list of name=value pairs, where values are tokens or quoted strings. Schemes that need to carry several distinct fields use this form:

Authorization: Digest username="ada", realm="api@example.com",
  nonce="dcd98b7102dd2f0e", uri="/v1/orders", qop=auth, nc=00000001,
  cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1",
  opaque="5ccc069c403ebaf9f0171e9517f40e41"

Parameter names are case-insensitive; values in quotes may contain characters a bare token may not. A header in this form may legally be folded across lines in the way shown above, though modern practice is to send it on one line.

One header, one scheme. If you need to present credentials to both a proxy and an origin server, that is what the separate Proxy-Authorization header is for — see below. Sending two Authorization headers is undefined behaviour, and what a server does with it ranges from taking the first, to taking the last, to joining them with a comma and failing to parse the result.

The scheme registry

IANA maintains the list of registered HTTP authentication schemes. Most developers meet two of them; the rest are worth recognising when they appear in a WWW-Authenticate challenge you did not expect.

SchemeDefined byCredential formWhat it carries
BasicRFC 7617token68Base64 of user:password — encoded, not encrypted
BearerRFC 6750token68An OAuth 2.0 access token; possession alone authorises
DigestRFC 7616auth-paramsA hash over a server nonce, so the password never crosses the wire
NegotiateRFC 4559token68A base64 SPNEGO token wrapping Kerberos or NTLM
HOBARFC 7486token68A digital signature bound to the origin, replacing passwords entirely
MutualRFC 8120auth-paramsPassword-based mutual authentication where the server also proves itself
SCRAM-SHA-256RFC 7804auth-paramsA salted challenge–response exchange over several round trips
vapidRFC 8292auth-paramsIdentifies an application server to a Web Push service

Basic

The credential is base64(username + ":" + password). Base64 is an encoding, not a cipher — anyone who sees the header can recover the password instantly, which you can demonstrate to a sceptical colleague in one step with the Base64 decoder. Basic is therefore acceptable only over TLS. The colon is a true delimiter, so a username containing one is unrepresentable; passwords may contain colons freely, since only the first separates the two fields. RFC 7617 added a charset="UTF-8" parameter to the challenge, which is how a server signals it will handle non-ASCII characters predictably.

Bearer

The dominant scheme in modern APIs, and the simplest possible model: whoever holds the token gets the access, exactly like a train ticket. The token may be a JWT the server validates by signature, or an opaque string it looks up in a store — the header cannot tell you which, and does not need to. RFC 6750 also defines two additional transports, a form field and a query parameter, both of which should be avoided because URLs end up in access logs, browser history and Referer headers. The bearer token guide covers lifetime, storage and revocation.

Digest

Designed for a world without ubiquitous TLS. The server issues a nonce in its challenge and the client returns a hash over the nonce, the credentials, the method and the URI, so the password itself never travels. RFC 7616 modernised it with SHA-256 and added the userhash parameter to hide the username too. It survives in embedded devices, routers and some enterprise systems, but it is rarely chosen for a new API: TLS solves the eavesdropping problem more comprehensively, and Digest requires the server to hold a password-equivalent value, which conflicts with modern password storage.

Negotiate, HOBA, Mutual and SCRAM

Negotiate is what single-sign-on inside a Windows domain looks like on the wire: a base64 SPNEGO blob wrapping Kerberos or NTLM, exchanged over several round trips and bound to the connection rather than to the request. HOBA replaces the password with a client-held key pair and an origin-bound signature. Mutual lets both sides prove knowledge of a password without either transmitting it, defeating phishing. SCRAM-SHA-256 brings the salted challenge–response mechanism familiar from databases and mail servers to HTTP. All four are genuinely registered and all four are rare in public APIs; recognise them, and reach for the relevant RFC only if a challenge names one.

Schemes that are not registered but are everywhere

Nothing stops a service defining its own scheme name, and several large ones have. These are not standards, but they are common enough that you will meet them:

  • AWS4-HMAC-SHA256 — Signature Version 4, and the best real-world example of the auth-param form. The credential is a structured list: Credential=AKIA…/20260720/eu-west-1/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=fe5f80f7…. The signature covers the method, path, query, a named set of headers and a hash of the body, so the request cannot be altered in transit — a very different security property from a bearer token, and the reason a mis-sorted header list breaks the whole thing.
  • Bot — used by Discord for bot tokens, where sending the same token as Bearer fails.
  • Token and ApiKey — conventions in various APIs for a long-lived key.

The lesson is procedural rather than technical: read the specific API's documentation for the exact scheme string. A server comparing against a scheme name it did not expect rejects a perfectly valid credential, and the error message almost never mentions the scheme. If you are checking a key against a provider right now, the API key tester pre-fills the correct shape for several common services.

The challenge–response flow

HTTP authentication is not something a client is supposed to guess at. The protocol defines a negotiation: the client asks, the server refuses and says what it would accept, the client asks again with credentials.

GET /v1/orders HTTP/1.1
Host: api.example.com

→ 401 Unauthorized
  WWW-Authenticate: Bearer realm="api", scope="orders.read"

GET /v1/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9…

→ 200 OK

A 401 must carry a WWW-Authenticate header. This is a requirement of the specification and it is very widely ignored. The header is what makes the status actionable: without it the client knows only that something was wrong, and with it the client knows which scheme to use and which protection space it belongs to.

You can watch the whole exchange in about twenty seconds: send an unauthenticated GET from the free API tester to any protected endpoint and read the WWW-Authenticate header on the 401 that comes back. Whatever it names is the scheme the server will accept, which is more reliable than the documentation on a surprising number of APIs.

A server may offer several challenges, either as repeated header fields or as one comma-separated list, in order of preference. The client picks the strongest scheme it supports. This is also the mechanism behind the browser password prompt: a plain Basic challenge is what makes a browser show its native login dialog, which is precisely why APIs consumed by JavaScript often avoid the Basic scheme name — nobody wants a native dialog appearing over their application.

Challenge parameters

ParameterApplies toMeaning
realmAny schemeNames the protection space, so a client knows which credential applies. Shown to users in browser prompts
charsetBasicOnly UTF-8 is defined; signals how non-ASCII credentials are encoded
errorBearerinvalid_request, invalid_token or insufficient_scope
error_descriptionBearerHuman-readable detail — for developers, never for parsing
scopeBearerThe scopes required for this resource
nonce, qop, algorithm, opaqueDigestInputs the client must fold into its response hash

The Bearer error parameters are the most useful and most neglected diagnostic in API authentication. error="invalid_token" tells a client its token is expired or malformed and it should refresh; error="insufficient_scope" with a scope parameter tells it the token is fine but lacks a permission, and names the missing one. RFC 6750 pairs the latter with 403 rather than 401, which is the clearest expression of the distinction: 401 means the server does not know who you are, 403 means it does and is refusing anyway. Retrying a 403 with the same credential is pointless by definition.

One more header closes the loop. Authentication-Info lets a server return authentication data alongside a successful response — Digest uses it to prove it knew the password too, and to hand over the next nonce so the following request avoids an extra round trip.

Proxy-Authorization: the same grammar, a different audience

Everything above concerns the origin server. Intermediaries have a parallel set of headers, with identical syntax and a completely separate purpose.

Origin serverProxy
Challenge status401 Unauthorized407 Proxy Authentication Required
Challenge headerWWW-AuthenticateProxy-Authenticate
Credential headerAuthorizationProxy-Authorization
ScopeEnd to end — forwarded onwardsHop by hop — consumed and removed

The last row is the substantive difference. Authorization is an end-to-end field: an intermediary passes it along untouched, because it is addressed to the origin. Proxy-Authorization is hop-by-hop: the proxy it is addressed to consumes it and does not forward it, so the origin never sees your proxy password. A single request can carry both headers with entirely unrelated credentials — corporate proxy on one, API token on the other — and that is exactly the intended design.

Practically, a 407 in your logs is not your API rejecting you. It is a network intermediary between you and it, which is why the fix lives in proxy configuration rather than in your token handling.

Where the header gets removed without telling you

Cross-origin redirects

Send an authenticated request, receive a 302 to another host, and the follow-up request will very likely go out without your Authorization header. Browsers strip it. curl strips it unless you explicitly pass --location-trusted. Most mainstream HTTP libraries strip it.

This is correct behaviour and worth understanding rather than working around. A redirect is an instruction from one server naming a URL of its choosing. If credentials were forwarded automatically, any server you authenticate to could redirect you to a host it controls and harvest your token — turning an open redirect from a nuisance into full credential disclosure. Because the client cannot know whether the new host is trustworthy, it assumes it is not.

The symptom is distinctive: a request that returns 401 even though the identical credential works elsewhere, with a redirect somewhere in the chain. Common triggers are an http URL upgrading to https, an apex domain redirecting to www, and a missing trailing slash. The fix is to request the final URL directly, not to relax the rule. If a redirect genuinely must carry credentials, the destination should be a host you control and you should be re-adding the header knowingly.

Cross-origin requests from a browser

In a browser, setting Authorization on a cross-origin fetch takes the request out of the CORS simple category and forces an OPTIONS preflight — and the preflight itself carries no credentials at all. The server must both permit the header by name in Access-Control-Allow-Headers and answer the OPTIONS without demanding authentication, or the real request is never sent. CORS explained covers the full sequence.

Shared caches

A request carrying Authorization is, by default, not stored by shared caches — RFC 9111 makes that the rule, on the sound assumption that an authenticated response is about one user and must not be served to another. A response may opt back in with explicit directives such as public or s-maxage, and doing so without thinking is a good way to serve one customer's data to the next. Private caches in the user's own browser are not affected.

Logs and proxies

Some intermediaries strip or rewrite headers they do not recognise, and some log them. A credential in a URL is logged everywhere, which is the main argument for keeping it in a header — though a header is only as private as your logging configuration, so redact Authorization explicitly rather than hoping. It is also worth knowing that an oversized header — a very large JWT plus a long cookie — can be refused with 431 Request Header Fields Too Large by a server whose buffer it exceeds.

Diagnosing a malformed header

Before assuming the credential is wrong, rule out the envelope. These are ordered roughly by how often they turn out to be the answer.

Header sentProblemCorrect form
Authorization: eyJhbGci…No scheme at allAuthorization: Bearer eyJhbGci…
Authorization: Bearer Bearer eyJ…Scheme included in the stored token as wellStore the token alone; add the scheme when building the header
Authorization: Bearer eyJ…Two spaces — the credential now begins with a spaceExactly one space
Authorization: Bearer eyJ…\nA newline pasted in from a wrapped terminalTrim the value before setting the header
Authorization: Basic ada:hunter2Base64 encoding skippedBase64 the user:password pair first
Authorization: Basic YWRhOmh1bnRlcjI=\n…Base64 tool wrapped its output across linesEncode without line wrapping
Authorization: Bot <token> sent as BearerWrong scheme for that APIUse the exact scheme string the provider documents
Header set but never arrivesStripped by a cross-origin redirect, or the client dropped it on a retryRequest the final URL directly; check the redirect chain

To settle it definitively, send the request from the API tester to an echo endpoint such as https://httpbin.org/headers and read back exactly what arrived — scheme, spacing and all. That one comparison separates “my header is malformed” from “my credential is rejected”, which are different problems with no overlap in their fixes. For the shape of the credential inside the header, continue to bearer tokens; for choosing between schemes in the first place, the API authentication guide compares all of them, and OAuth vs JWT untangles the two things most often confused with each other. Neighbouring header behaviour is covered in the HTTP headers reference and the Content-Type guide.

Frequently asked questions

What is the correct syntax of the Authorization header?

A case-insensitive scheme name, one space, then the credentials. RFC 9110 allows two credential shapes: a single token68 blob, used by Basic and Bearer, or a comma-separated list of name=value parameters, used by Digest and AWS Signature Version 4. The scheme is never optional.

Why did my Authorization header disappear after a redirect?

The client dropped it on purpose. Browsers, curl and most libraries strip Authorization when a redirect crosses to a different origin, because forwarding it would hand your credential to whatever host the first server named. Request the final URL directly rather than relaxing the rule.

What is the difference between 401 and 403?

401 means unauthenticated — the server is challenging you, which is why it must send WWW-Authenticate. 403 means unauthorised — it knows who you are and is refusing anyway, so the same credential will not help on a retry.

What does the realm parameter in WWW-Authenticate mean?

It names a protection space on that origin so a client knows which stored credential applies; paths sharing a realm accept the same credentials. It is the one parameter defined for every scheme, and because browsers display the string in their prompt it should be short and meaningful.

How is Proxy-Authorization different from Authorization?

It authenticates you to an intermediary rather than the origin. Same grammar, but the proxy consumes and removes it instead of forwarding it, and a proxy demanding credentials answers 407 with Proxy-Authenticate. One request may carry both headers with different credentials.

Can I send an API key in the Authorization header?

Yes, though the scheme varies: some APIs define Token or ApiKey, some ask you to send the key as a Bearer token, and some prefer a custom header such as X-API-Key. Follow the documentation exactly — a server matching an unexpected scheme name rejects a valid key.