What Is a
Bearer Token?
How the Authorization: Bearer header works, how bearer tokens compare to API keys and session cookies, and how to store and transmit them without leaking them into your logs.
What is a bearer token: "bearer" means whoever holds the token gets access — there's no additional proof of identity required, which makes bearer tokens simple and portable but also means a stolen token grants full access until it expires. The name comes from RFC 6750, the OAuth 2.0 bearer token standard.
Bearer tokens in your logs?
Authorization headers end up in access logs more often than people realise. Use the Log Sanitizer to redact them before sharing logs anywhere.
The Authorization Header
Bearer tokens travel in the HTTP Authorization request header. The format is standardised by RFC 6750 and looks like this:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
The structure has two parts separated by a single space: the scheme (Bearer) and the token itself. The scheme name is case-insensitive in the RFC but Bearer with a capital B is the conventional spelling virtually every server and client library expects.
Other authentication schemes exist and use the same header field — Basic (base64-encoded username:password), Digest (challenge-response), and AWS4-HMAC-SHA256 (AWS Signature Version 4) are the most common alternatives. But Bearer is the dominant scheme for modern REST APIs and everything built on OAuth 2.0.
Common Authorization schemes
| Scheme | Used for | Standard |
|---|---|---|
| Bearer | OAuth 2.0, most REST APIs | RFC 6750 |
| Basic | Simple username/password | RFC 7617 |
| Digest | Challenge-response auth | RFC 7616 |
| AWS4-HMAC-SHA256 | AWS service requests | AWS docs |
How Bearer Tokens Are Used — the Request Lifecycle
The typical OAuth 2.0 flow using bearer tokens follows four steps:
- Client authenticates. The client sends credentials to a token endpoint — this might be a username and password, an API key, a client ID and secret (client credentials flow), or a social login redirect. The exact method depends on the OAuth grant type.
- Server returns a bearer token. On success, the server issues a token — usually a JWT for stateless APIs or an opaque random string for stateful ones — along with an expiry time and often a refresh token.
- Client includes the token in every subsequent request. For the lifetime of the token, the client attaches it in the
Authorization: Bearer <token>header on every API call. No further credential exchange is needed. - Server validates the token on each request. For JWTs, the server verifies the cryptographic signature and checks the
exp(expiry) andaud(audience) claims — no database lookup required. For opaque tokens, the server queries its token store or calls an introspection endpoint to check validity.
This split — authenticate once, then use a token — is what makes bearer tokens practical at scale. The server doesn't need to look up a user record or rehash a password on every request; it just verifies the token.
Bearer Token vs API Key vs Session Cookie
All three are credentials that prove who's making a request. They differ in how they're transmitted, how they expire, and how they can be revoked:
| Property | Bearer token | API key | Session cookie |
|---|---|---|---|
| Transmitted in | Authorization header | Header or query param | Cookie header |
| Format | Usually a JWT | Opaque string | Session ID |
| Expires | Yes (exp claim or TTL) | Often doesn't | Configurable |
| Revocable | Depends — JWT: no; opaque: yes | Yes | Yes |
| Visible to JS | If stored in localStorage | If embedded in code | Only if not httpOnly |
| Standard | RFC 6750 | No standard | RFC 6265 |
The key practical difference: API keys are designed for server-to-server calls where a long-lived credential is acceptable. Bearer tokens are designed for delegated access — a user grants an app permission to act on their behalf, and the token encodes that grant with an expiry baked in. Session cookies sit in the middle, typically used for browser sessions where the server maintains state.
Security Risks
Token in the URL
The single biggest bearer token mistake is putting the token in a query parameter rather than the Authorization header:
GET /api/data?access_token=eyJhbGci... ← never do this
URLs appear verbatim in server access logs, browser history, bookmarks, and Referer headers sent to third-party resources on the page. A token embedded in a URL will be logged in plain text by every proxy, load balancer, and CDN the request passes through. RFC 6750 mentions this as a last-resort transmission method, but every practical guide treats it as off-limits.
Token in localStorage
localStorage is readable by any JavaScript running on the page. A single successful XSS injection — one rogue script tag, one compromised third-party dependency — can exfiltrate every token in localStorage in milliseconds. If the token has a long expiry, the attacker has time to use it long after the page visit ended. For browser apps handling sensitive data, localStorage is not an acceptable token store.
Token in logs
Most web servers and reverse proxies log the full request headers by default. That means Authorization: Bearer <token> ends up in your access log on every authenticated request. A typical Nginx or Apache access log line looks like:
203.0.113.42 - - [02/May/2026:14:23:01 +0000] "GET /api/profile HTTP/1.1" 200 842
"https://app.example.com/dashboard"
"Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMzQ1IiwiZXhwIjoxNzQ2MjkwNTgxfQ.sig"
Anyone with read access to your log files — a developer, a support engineer, a log aggregation service — can see and reuse active tokens. If the token is a JWT, they can also decode the payload without a key, which may expose user IDs, email addresses, roles, and other claims. The log sanitization guide covers this in more detail.
Long-lived tokens
The longer a token's expiry window, the longer a stolen token is useful to an attacker. A 30-day access token leaked into a log gives an attacker 30 days of access before natural expiry. The industry norm for access tokens is 15 minutes to 1 hour, with a longer-lived refresh token (days to weeks) used only to obtain new access tokens — and only over a direct, authenticated channel, not stored in a place JavaScript can reach.
How to Store Bearer Tokens Safely
httpOnly cookie (preferred for browser apps)
An httpOnly, Secure, SameSite=Strict cookie is the best storage for browser-facing bearer tokens. httpOnly makes the cookie completely invisible to JavaScript — no XSS payload can read it. Secure ensures it only travels over HTTPS. SameSite=Strict prevents it from being sent in cross-site requests, which blocks CSRF. The tradeoff is that your JavaScript can't read the token value, so you can't inspect claims client-side without a separate mechanism (like a non-sensitive identity cookie the JS can read).
Memory only (for SPAs)
For single-page applications that need JavaScript access to the token, storing it in a module-scoped variable (in memory only) is safer than localStorage. The token is never written to disk, never persists across page reloads, and is not reachable by XSS payloads that can only read storage APIs. The cost is that the user has to re-authenticate on every page refresh, which is mitigated by a silent token refresh in the background via an httpOnly refresh token cookie.
Never localStorage for sensitive tokens
To be explicit: do not store access tokens for sensitive resources in localStorage or sessionStorage. Both are accessible via window.localStorage and window.sessionStorage from any script on the page. If your app includes third-party analytics, ads, or a compromised npm dependency, those scripts can reach your token store.
How Bearer Tokens Appear in Logs
Even when you've done everything else right, bearer tokens routinely surface in logs because Authorization headers are captured by default. Here's a realistic combined-format access log line from a Node.js/Express app using Morgan:
203.0.113.42 - alice [02/May/2026:09:41:17 +0000] "GET /api/v1/users/me HTTP/1.1" 200 1247 "-" "Mozilla/5.0" Authorization:"Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQ1NiIsImVtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3NDYyOTM2Nzd9.redacted"
This single line exposes the user's IP address, their username, the JWT itself (which anyone can base64-decode to see the email and role claims), and the exact expiry timestamp. If this log file is shared with a third party — pasted into an AI chat, attached to a support ticket, uploaded to a log aggregation service — all of that is exposed. Use the Log Sanitizer to redact Authorization headers and tokens before sharing any log output.
How to Redact Bearer Tokens from Logs
If you want to strip bearer tokens at the source before they're written, here are two common patterns.
sed one-liner — useful for scrubbing an existing log file:
sed -E 's/(Authorization: Bearer )[A-Za-z0-9._~+/=-]+/\1[REDACTED]/g' access.log
Python logging.Filter — redacts tokens before any handler writes the record, so they never reach disk or stdout:
import logging, re
class BearerTokenFilter(logging.Filter):
_pattern = re.compile(
r'(Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+',
re.IGNORECASE
)
def filter(self, record):
record.msg = self._pattern.sub(r'\1[REDACTED]', str(record.msg))
if record.args:
record.args = tuple(
self._pattern.sub(r'\1[REDACTED]', str(a))
if isinstance(a, str) else a
for a in (record.args if isinstance(record.args, tuple)
else (record.args,))
)
return True
# Attach to the root logger or any specific logger
logging.getLogger().addFilter(BearerTokenFilter())
The filter pattern is the same approach described in the PII redaction in Python guide. Attach it at the root logger level to catch all handlers — file, stdout, and any third-party logging integrations — in one place.
Ready to Scrub Tokens from Your Logs?
Redact Bearer Tokens from Your Logs
Paste the log. Authorization headers and tokens are redacted instantly, in your browser.
Open the Log Sanitizer →Related Reading
- Log Sanitizer tool — redact bearer tokens, emails, IPs and API keys from logs in your browser, with no uploads.
- Log sanitizer without uploading — why uploading logs to sanitize them defeats the purpose, and what to look for in a client-side tool.
- How to sanitize log files — a step-by-step walkthrough of the sanitization workflow for real production logs.
- HIPAA-compliant log redaction — what counts as PHI in a log file and how to strip it, including Authorization headers.
- How to redact PII in Python — logging.Filter patterns for removing tokens, emails, and other sensitive values before they reach any handler.
- JWT vs session tokens — a deeper comparison of stateless JWTs and stateful session tokens, covering signing algorithms, claim validation, and revocation strategies.