Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Explainer

The HTTP Authorization Header: Schemes, Bearer Tokens, and What Not to Do

Sean

Platform Writer

Aug 26, 2026
7 min read

The Authorization header carries a scheme name followed by credentials: Authorization: Bearer <token> or Authorization: Basic <base64>. The scheme word is mandatory and the space matters. Basic is base64 encoding, not encryption — anyone who sees the header can read the password, which is why it requires HTTPS to be meaningful at all.

The HTTP Authorization Header: Schemes, Bearer Tokens, and What Not to Do

This header is straightforward and consistently misused. The recurring problems are treating base64 as a security measure, trying to put two schemes in one header, and losing the header at a proxy or a redirect without noticing.

Table of contents

The format

Authorization: <scheme> <credentials>

The scheme is a registered keyword and the credentials are formatted according to it. Both parts are required, separated by a single space.

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Authorization: Digest username="bob", realm="api", nonce="..."
Authorization: Negotiate YIIZ...
Authorization: AWS4-HMAC-SHA256 Credential=..., Signature=...

The scheme name is case-insensitive per the specification, but many servers compare it literally. Send Bearer, not bearer, and you avoid the class of bug where an API works with one client library and not another.

Sending the token without the scheme is the single most common mistake:

Authorization: eyJhbGciOiJIUzI1NiIs...     wrong -- no scheme
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...   correct

The scheme tells the server how to interpret what follows. Without it, a compliant server has no basis for parsing the value, and the error you get back is usually just a 401 with no explanation.

Basic authentication is not encryption

Basic takes username:password, base64-encodes it, and sends it:

echo -n 'username:password' | base64
# dXNlcm5hbWU6cGFzc3dvcmQ=

echo 'dXNlcm5hbWU6cGFzc3dvcmQ=' | base64 -d
# username:password

Base64 is an encoding, reversible by anyone, with no key involved. Over plain HTTP, Basic auth transmits the password in effectively clear text to every intermediary on the path.

It is acceptable over HTTPS, where TLS provides the confidentiality. It remains a poor choice for anything user-facing because the credentials are sent on every single request, meaning a long-lived secret crosses the network constantly rather than once.

Where Basic is genuinely reasonable:

  • Server-to-server calls over TLS with a rotatable credential rather than a user password.
  • A quick internal tool where the alternative is no authentication at all.
  • Registry and package-manager authentication, where it is the established convention.

The WWW-Authenticate response header is how a server requests it, and it is what triggers the browser’s built-in login prompt:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Restricted Area"

Bearer tokens, and what bearer means

Bearer is the scheme used by OAuth 2.0 and most modern APIs. The name is the security model: whoever bears the token gets the access. There is no proof of identity beyond possession.

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0.abc

That has direct consequences for how tokens must be handled:

  • Never in a URL. Query strings appear in server logs, browser history, and the Referer header sent to third parties. A token in a URL is a token that has been written down in several places you do not control.
  • Short expiry. A stolen token is valid until it expires. Fifteen minutes with a refresh token is a common shape; a token that never expires is a permanent credential.
  • HTTPS only. A bearer token over plain HTTP is readable by anything on the path.
  • Not in localStorage for browser applications, where any injected script can read it. An httpOnly cookie is not reachable from JavaScript.

A JWT is a common bearer token format, and it is worth being clear that a signed JWT is not encrypted. Anyone can decode the payload:

echo 'eyJzdWIiOiIxMjM0In0' | base64 -d
# {"sub":"1234"}

The signature proves the payload was not altered. It does not hide it. Do not put anything confidential in a JWT payload.

Two schemes, one header

A recurring requirement: an API gateway wants Basic and the application behind it wants Bearer. HTTP does not accommodate that cleanly.

The header can technically carry a comma-separated list, and in practice almost nothing parses it correctly:

Authorization: Basic dXNlcjpwYXNz, Bearer eyJhbGc...    fragile

The workable approaches, in order of preference:

  1. Use a second header. X-Api-Key alongside Authorization, or a purpose-named header for the inner credential.
  2. Terminate one at the proxy. The gateway validates Basic, strips it, and forwards only the Bearer token. This is the cleanest arrangement.
  3. Rewrite at the proxy. nginx can move a custom header into Authorization for the upstream.
location /api/ {
    proxy_set_header Authorization $http_x_app_token;
    proxy_pass http://backend;
}

That takes the client’s X-App-Token and presents it as Authorization to the backend, which lets the gateway keep its own scheme on the outer request.

Where the header disappears

Several things strip Authorization silently, and the resulting 401 gives no clue that the header never arrived.

Redirects. Most HTTP clients drop the header when a redirect crosses to a different host, which is correct behaviour — forwarding credentials to a host you did not intend to authenticate against would be a serious vulnerability. curl -L follows redirects but will not resend the header cross-host unless you pass --location-trusted, which you should be reluctant to do.

Apache with CGI or FastCGI does not pass it through by default:

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
# or
CGIPassAuth On

This is the classic cause of a PHP API that works on one host and returns 401 on another with identical code.

CORS preflight. Authorization is not a simple header, so sending it triggers a preflight, and the server must list it in Access-Control-Allow-Headers or the browser blocks the real request entirely.

Confirm what actually arrived rather than what you sent:

curl -v -H "Authorization: Bearer test" https://api.example.com/me 2>&1 | grep -i auth

And on the server side, log the presence of the header — never its value — while debugging. A log line containing a live token is a credential leak into your logging system.

How this fits the rest of the stack

Every practical rule here reduces to the same idea: the token is the credential, so anywhere it gets written down is somewhere it can be stolen. URLs, logs, browser storage and error reports are all places tokens end up by accident rather than by decision.

Keeping them out of those places starts with keeping them out of the code. RunxBuild holds environment variables per service, so API keys and signing secrets are configured on the service rather than committed to the repository or baked into an image — and the runtime logs you read while debugging are separate from the place the secret lives. If you are sizing a service alongside a managed database, the RunxBuild hosting calculator shows them as separate line items.

Useful related references:

FAQ

What is the format of the HTTP Authorization header?

Authorization: <scheme> <credentials> — a registered scheme name, one space, then credentials formatted for that scheme. The most common are Bearer <token> and Basic <base64>. Omitting the scheme word is the most frequent mistake and usually produces an unexplained 401.

Is Basic authentication secure?

Only over HTTPS, and even then it is a weak choice for user-facing systems. Base64 is encoding, not encryption — anyone who sees the header can decode the password. It also sends the credential on every request rather than once, so the secret crosses the network constantly.

Why is it called a Bearer token?

Because whoever bears it gets the access — possession is the entire proof. There is no additional identity check, which is why bearer tokens must never appear in URLs, must use short expiry with refresh, and must only travel over HTTPS.

Can I send two Authorization schemes in one request?

Not reliably. The syntax technically permits a comma-separated list but almost no server parses it correctly. Use a second header such as X-Api-Key, or have the proxy validate its scheme and forward only the inner credential.

Why does my Authorization header disappear?

Most likely a cross-host redirect, which HTTP clients drop the header on deliberately, or Apache with CGI not passing it through — fix that with CGIPassAuth On. In browsers, Authorization triggers a CORS preflight and must be listed in Access-Control-Allow-Headers.

#http authorization header#http#bearer token#authentication#api