RFC 6750 permits an access token in a URI query parameter (`?access_token=...`) but strongly discourages it. What are the leakage paths, and what does correct carriage look like instead?
answer
- URL = logged, bookmarked, Referer'd, pasted, cached
- header not in request target → not in logs/history by default
- scrub authorization/cookie in logs, APM, HAR
- Cache-Control: no-store on authenticated responses
- forced into a URL? mint a short-lived single-purpose token
basics
~20 sURLs are recorded everywhere: server and proxy access logs, browser history, bookmarks, Referer headers to third parties, shared links, crash reports, CDN analytics. Use Authorization: Bearer, add Cache-Control: no-store, and redact the header from all logging.
solid answer
~60 sA token in the query string inherits every place a URL is written down. Access logs on every hop record the full request target by default. Browsers keep it in history and may send it in a `Referer` header to any third-party resource the page loads. Users copy and paste URLs into tickets and chat. CDNs and analytics key on full URLs, and error trackers attach them to reports. Caches may also key on the URL, so a token can end up in a shared cache entry. The header avoids most of this because headers are not part of the request target and are not stored by default — but not automatically: you must scrub `Authorization` from access logs, tracing spans, APM payloads and HAR exports, since many stacks capture headers verbatim. Also: mark responses to authenticated requests `Cache-Control: no-store`; never echo the token into a response body or an error message; and rely on the rule that browsers drop `Authorization` on cross-origin redirects rather than re-attaching credentials yourself.
code
http · 8 linesGET /v1/orders?access_token=mF_9.B5f-4.1JqM HTTP/1.1
Host: api.example.com
# request target — recorded verbatim by access logs, proxies, history, Referer
GET /v1/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
# header — not part of the target, not logged unless something captures headersgo deeper
Say that URLs get logged, bookmarked and shared, so the token belongs in the Authorization header, sent over TLS.
Enumerate the concrete paths — access logs, proxies, browser history, Referer, shared links, caches — and add the no-store requirement.
Cover the residual header risks: log and APM scrubbing, HAR exports, redirect credential-stripping, audience binding of clients, and short-lived single-purpose tokens for header-less contexts.
Make it policy: a fleet-wide redaction standard applied at gateway and instrumentation level, a rule that credentials never appear in request targets, and a token-minting facility for the cases that cannot set headers.
## The three carriage methods, ranked RFC 6750 defines three ways to send a bearer token: 1. **`Authorization: Bearer <token>` header** — mandatory for servers to support, and the only one that should be used. 2. **Form-encoded body parameter `access_token`** — allowed only for `application/x-www-form-urlencoded` bodies, intended for clients that cannot set headers. Bodies are less commonly logged than URLs, but this method also forbids caching-friendly requests and is rarely worth it. 3. **URI query parameter `access_token`** — explicitly a last resort, and later OAuth security guidance moves further, telling servers not to accept it at all. ## Where a token in a URL ends up The request target is treated as identifying data, not secret data, by essentially the whole HTTP ecosystem: - **Access logs.** Nginx, Apache, load balancers, API gateways and application frameworks all log the full path and query by default. Those logs are shipped to a central store that far more people can read than can read production secrets, and they are retained for months. - **Intermediary logs.** Forward proxies, corporate TLS-inspecting middleboxes, service-mesh sidecars and CDNs record URLs. Even under TLS the origin-side hops see them. - **Browser history and bookmarks.** Anyone with the device — or a synced profile — can read the token back long after the session ended. - **The `Referer` header.** When a page loaded from a URL containing the token fetches a third-party script, image, font or beacon, the browser may transmit the whole URL, token included, to that third party. `Referrer-Policy` mitigates it but is a per-site setting you now depend on. - **Copy-and-paste.** Users share URLs in tickets, chat, screenshots and bug reports without any sense that they are pasting a credential. - **Caches.** Shared caches key on the URL. A cached entry can be served to another user, and the cache itself now stores the token as part of its key. - **Error and analytics tooling.** Crash reporters, RUM scripts and product analytics attach the current URL to every event, sending the token to vendors. Any one of these turns a short-lived credential into a durable, widely-copied one — and unlike a password, nobody thinks to rotate it, because it does not look like a secret. ## Why the header is better, and where it still leaks Headers are not part of the request target: they are not written to standard access-log formats, not kept in browser history, never in `Referer`, and not part of a cache key. That removes most of the list above. It does not remove all of it. Concrete gaps to close: - **Verbose logging.** Debug-level HTTP client logging, request/response dumps and "log all headers" middleware capture `Authorization` verbatim. Redaction must be an allow-list or an explicit deny-list applied case-insensitively — `authorization`, `proxy-authorization`, `cookie`. - **Tracing and APM.** OpenTelemetry HTTP instrumentation can capture request headers; error reporters attach them to events. Configure the scrub list before you turn capture on. - **HAR files.** "Save all as HAR" from browser devtools includes every header. HARs get attached to support tickets routinely. Treat any HAR from an authenticated session as a credential dump. - **Response bodies.** Never echo the token into a debug endpoint, an error payload or a rendered page. - **Caching.** RFC 6750 requires servers to include `Cache-Control: no-store` on responses to authenticated requests, so private data and any token material never land in a shared or disk cache. - **Redirects.** Browsers and well-behaved clients drop `Authorization` when a redirect crosses origins, precisely so a manipulated `Location` cannot exfiltrate credentials. Do not defeat that by manually re-attaching the header after following a redirect. - **Third-party origins.** An HTTP client configured with a global default `Authorization` header will send your token to every host it touches, including CDNs and analytics endpoints. Bind the credential to its audience. ## When you are forced into a query parameter Some contexts genuinely cannot set headers — a `<img>` or `<video>` `src`, an `EventSource` connection, a browser-initiated file download, a third-party webhook validator. The right answer is not to put the real access token in the URL. Instead mint a **separate, single-purpose credential**: short-lived (seconds to minutes), bound to that one resource and method, single-use where possible, and useless for anything else. Then even a logged URL exposes a token that expired before anyone read the log. Treat any such token as compromised on use and never reuse the general-purpose access token in this role. ## The summary line Query parameters are for identifying resources, headers are for credentials. Everything in HTTP that records requests records URLs; almost nothing records the `Authorization` header unless you tell it to — so use the header, and then check what your logging, tracing and devtools exports are actually capturing.
- A browser must load an authenticated image or open an EventSource connection, and cannot set headers. What do you do?Do not put the general-purpose access token in the URL. Mint a separate short-lived credential scoped to that single resource and method — seconds to minutes of validity, ideally single-use — and put that in the query string. A leaked log line then contains something already expired and useless for anything else.
- Using the Authorization header keeps tokens out of the URL. What still has to be done to prevent leakage?Scrub the header in access logs, HTTP-client debug logging, tracing spans and error reporters — many stacks capture headers verbatim once you raise the log level. Set `Cache-Control: no-store` on authenticated responses, never echo the token into a body or error message, treat HAR exports as credential dumps, and keep the credential bound to its audience so a client does not attach it to third-party hosts.
saying these in an interview costs you the question
- "It's fine, the URL is inside TLS" — ignoring that both endpoints and every log on the origin side record it
- Forgetting Referer leakage of the full URL to third-party resources on the page
- Assuming header carriage is automatically safe without checking logging, tracing and HAR capture
- Omitting Cache-Control: no-store on responses to authenticated requests
- Solving the header-less case by putting the long-lived access token in the query string