Fix guide · medium · api_key_in_url
API key passed in URL query string
An API-key-shaped query parameter (api_key=, apikey=, etc.) was found in the bundle. URLs ride in too many side channels (logs, history, Referer) for credentials to live there.
Why it matters
Same fundamental leak surface as auth tokens (see /fix/auth_token_in_url) — server logs, browser history, Referer headers, analytics. The severity is lower because:
- The "API key" might be a public publishable key (Stripe pk_, Algolia search-only) where leaking is by design.
- The "API key" might gate only rate limits, not auth — so its leak doesn't directly compromise user data.
But it's not zero-risk:
- If the key is private, it's a credential leak, full stop.
- Even public keys benefit from being in headers — fewer places to harvest them, easier to rotate, doesn't burn the URL slot for parameters that *should* be in URLs (search queries, pagination).
- Some "public" keys can be abused for cost — a leaked Stripe publishable key isn't an account-takeover, but a leaked Algolia search-only key with a high-volume index can run up your bill via amplification attacks.
Decision tree:
- Public-by-design key (Stripe pk_, Algolia search-only)? Move it to a header anyway, but don't panic.
- Private key, full read access? Treat it as compromised, rotate, switch to header-based auth.
- Don't know? Ask your provider's support whether the key in question has any privileged scope. Most providers tell you in the dashboard which keys are "public" vs "secret".
How to fix it
1. Move it to a header. Most APIs accept their key as a header in addition to query params:
fetch('https://api.example.com/things', {
headers: { 'x-api-key': 'YOUR_KEY' }
});
2. If it's a private key, rotate it. Get a new one from the provider, update your config, deploy. Treat the leaked one as compromised — assume someone has it.
3. If it's a public key, audit your assumption. Many "public" keys have hidden modes that aren't actually public (Algolia "search-only" keys can mutate analytics endpoints; Stripe "publishable" keys can list customers in some old configurations). Read the provider docs.
4. For provider APIs you can't change (an API that only accepts query-string keys), proxy through your own backend. The browser hits /api/proxy/things; your backend adds the key and forwards. Now the key only lives server-side.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection