BYOKchat Blog

Why API Keys Should Never Be Put in URLs

Understand why putting API keys in query strings or paths leaks credentials through logs, histories, analytics, referrers, screenshots, and infrastructure, and what to do instead.

· 6 min read

On this page
  1. A URL crosses more systems than you think
  2. Query strings are especially easy to leak
  3. Paths are not safer than query parameters
  4. URLs are designed to be shareable identifiers
  5. Authentication headers are easier to classify
  6. Custom endpoints make this more dangerous
  7. Redirects can expose URL credentials
  8. Referrer behavior is another reason to keep secrets out
  9. Mobile apps still log URLs
  10. Shell commands can persist URL secrets
  11. Error messages often include URLs automatically
  12. Monitoring systems love URLs
  13. Caches and request keys may preserve URLs
  14. URLs may be included in backups
  15. Analytics should be able to keep endpoint metadata without secrets
  16. Use the provider’s supported authentication contract
  17. Keep credentials out of base URL fields
  18. Separate endpoint and authentication in code
  19. Redact URL parameters defensively anyway
  20. Testing the invariant
  21. A practical rule for BYOK clients
  22. Where BYOKchat fits
  23. Further reading

An API key in a URL is much harder to contain than the same key in a properly handled authorization header.

Bad:

https://api.example.com/v1/chat?api_key=SECRET

Also bad:

https://api.example.com/v1/SECRET/chat

Prefer the provider’s documented authentication mechanism, usually a header such as:

Authorization: Bearer SECRET

or a provider-specific secret header.

The reason is not that headers are magically private.

It is that URLs are routinely copied, logged, displayed, indexed, shared, cached, and attached to diagnostics by infrastructure that may never expect them to contain secrets.

A URL crosses more systems than you think

A request URL can appear in:

  • client debug logs;
  • reverse-proxy access logs;
  • load balancer logs;
  • CDN logs;
  • server traces;
  • analytics;
  • crash reports;
  • browser histories;
  • screenshots;
  • support tickets;
  • monitoring dashboards;
  • copied curl commands;
  • shell history;
  • network debugging tools.

Once a credential is embedded in the URL, every one of those becomes part of the secret boundary.

Query strings are especially easy to leak

Developers often think:

It's HTTPS, so the query string is encrypted in transit.

HTTPS protects traffic on the wire between TLS endpoints.

It does not stop the application, proxy, gateway, server, or observability stack from recording the URL after decryption.

The correct question is:

Which systems receive or record the URL as application metadata?

That list is usually long.

Paths are not safer than query parameters

Moving the credential from:

?api_key=SECRET

to:

/v1/SECRET/chat

does not fix the core problem.

Access logs commonly record paths too.

A secret should not be part of the request identifier.

URLs are designed to be shareable identifiers

A URL’s job is to identify a resource.

Developers routinely:

copy URL
paste URL
bookmark URL
print URL
log URL
attach URL to issue

A bearer credential has the opposite property:

show only to the systems that need authorization

Mixing these roles creates leaks.

Authentication headers are easier to classify

A networking stack can centrally recognize:

Authorization
x-api-key
api-key

as sensitive.

A logger can then redact them before recording request metadata.

For example:

Authorization: [REDACTED]

With URL credentials, the logger must parse and sanitize every possible path/query convention correctly.

That is much more error-prone.

Custom endpoints make this more dangerous

A multi-provider AI client may support custom endpoints and custom headers.

If the user enters:

https://example.com/v1?token=SECRET

as a base URL, that secret can leak through:

  • connection diagnostics;
  • provider settings export;
  • analytics;
  • URL validation errors;
  • UI screenshots;
  • backup files.

The client should treat endpoint identity and credential material as separate fields.

base URL -> ordinary configuration
credential -> secret storage

Redirects can expose URL credentials

A request can receive a redirect to another location.

If a credential is embedded in the URL, redirect behavior can become hard to reason about.

The app may:

  • preserve the query string;
  • construct a new URL from the old one;
  • log both locations;
  • surface the final URL in diagnostics.

Credentials should remain attached to the configured authentication policy, not to string manipulation of URLs.

Referrer behavior is another reason to keep secrets out

Web-style clients can propagate URL context through referrer mechanisms under some navigation/request scenarios.

Even when modern policies limit referrer data, the durable security rule is simpler:

Do not rely on referrer policy to protect a credential that should never have been in the URL.

A secret-free URL is safer than a secret URL plus many compensating controls.

Mobile apps still log URLs

Even if your native app is not a browser, it can still expose URLs accidentally.

Examples:

print(request.url!)
logger.error("Request failed: \(error) URL=\(url)")
Network inspector: GET /v1/chat?api_key=SECRET

If authentication is in a header, a redaction layer can remove it while leaving the useful URL intact.

Shell commands can persist URL secrets

A support guide might tell a user to test:

curl 'https://api.example.com/v1/models?api_key=SECRET'

That command may remain in shell history.

A header form is still sensitive, but at least it follows normal credential handling patterns:

curl \
  -H 'Authorization: Bearer SECRET' \
  https://api.example.com/v1/models

Support docs should avoid asking users to paste real credentials into shared logs regardless of mechanism.

Error messages often include URLs automatically

Framework errors can include:

  • request URL;
  • failing host;
  • redirect location;
  • route path.

If a secret is in the URL, sanitization must happen before the error reaches:

UI
logs
crash reporting
telemetry
support export

Keeping secrets out of URLs makes ordinary error reporting much safer.

Monitoring systems love URLs

Observability stacks often group requests by:

host
path
route
query
status
latency

That is exactly the metadata you want for debugging.

It becomes dangerous if the same fields also contain credentials.

A well-designed system can record:

POST /v1/chat/completions -> 429 in 812ms

without recording any key.

Caches and request keys may preserve URLs

Some HTTP components use URLs as cache keys or deduplication identifiers.

Even if a sensitive request is not cacheable, application code may still persist the URL for retry/recovery.

For example:

{
  "failed_request_url": "https://api.example.com/v1/chat?api_key=SECRET"
}

Now the credential has moved from request memory into local durable state.

URLs may be included in backups

A BYOK client’s backup format may legitimately include configured base URLs.

That is useful for restoring provider connections.

If the base URL itself carries a token, the backup suddenly contains a secret even though the export code intentionally excludes Keychain data.

This breaks a clean security invariant.

Prefer:

{
  "base_url": "https://api.example.com/v1",
  "credential": null
}

and request the credential again after restore.

Analytics should be able to keep endpoint metadata without secrets

A privacy-preserving client may store sanitized endpoint classes such as:

provider=custom
transport=https
private_network=false

It should not need the full user URL.

If the URL contains a key, even safe endpoint diagnostics become difficult.

See Privacy-Preserving Analytics for AI Apps.

Use the provider’s supported authentication contract

Common patterns include:

Authorization: Bearer <token>
x-api-key: <token>

or another documented header/token mechanism.

Do not invent a query parameter merely because it is easy to append.

If a provider explicitly requires query-based authentication, isolate that integration and treat the URL as sensitive everywhere. But that should be an exception driven by the external protocol, not your default client architecture.

Keep credentials out of base URL fields

When accepting a user-entered custom endpoint, validate for obvious credential material.

For example, flag URLs containing query keys such as:

api_key
token
access_token
secret
key

Do not pretend that heuristic detection is complete.

Instead, design the UI with separate fields:

Base URL
Authentication type
API key / token
Protected custom headers

That guides users toward a safer configuration.

Separate endpoint and authentication in code

Bad model:

struct Connection {
    var urlWithCredential: URL
}

Better:

struct Connection {
    var baseURL: URL
    var credentialID: UUID?
    var authKind: AuthKind
}

Then the request builder combines them only at send time.

Redact URL parameters defensively anyway

Even if your app never intentionally puts credentials in URLs, user input and third-party APIs can surprise you.

A sanitizer can remove suspicious parameters before logging:

https://example.com/callback?token=[REDACTED]&page=2

Treat this as defense in depth, not permission to use URL credentials deliberately.

Testing the invariant

Add tests such as:

provider connection export never contains known fake secret
request logs contain URL but not credential
redirect diagnostics redact sensitive query values
support bundle scanner finds no test credential
analytics schema has no raw_url field
backup scanner rejects secret-looking query parameters

A useful fake token:

TEST_API_KEY_URL_LEAK_SENTINEL_123456

Generate logs, backups, analytics, and crash breadcrumbs, then assert that string never appears.

A practical rule for BYOK clients

Use this policy:

Endpoint identity belongs in URL configuration.
Authentication belongs in protected credential storage.
Request construction joins them at the network boundary.
Diagnostics may show endpoint identity, never authentication.

That keeps security boundaries understandable.

Where BYOKchat fits

A multi-provider client needs to preserve useful custom endpoint diagnostics without turning every URL into secret data.

Keeping API keys and protected headers separate from base URLs makes it possible to:

  • store credentials in Keychain;
  • export provider configuration safely;
  • log sanitized endpoints;
  • restore connections without secrets;
  • inspect failures without leaking authorization.

That is a much stronger design than trying to sanitize credentials after they have already been embedded into URLs.

Further reading

Keep reading