Guide

Your API key is in the frontend. How to tell, and what to do next

If the browser can read it, so can everyone else. The order you fix it in matters more than the fix.

VibeLock8 min read

Short answer

Anything your app sends to the browser is public, including a key bundled into your JavaScript or set in an environment variable your framework exposes to the client. If a secret key is there, treat it as leaked: rotate it at the provider first, then move the code that needs it to your server, then check your repository history. Publishable keys that a provider designs for the browser are fine. A key that can spend money, read private data or act as an admin must never leave the server.

How do secret keys end up in the frontend?

Almost never on purpose. The common paths in an app built with an AI coding tool are:

  • A component calls a paid API directly, because that was the shortest way to make the feature work, so the key travels with the component.
  • A key is stored in an environment variable with the prefix your framework uses to mark values as public. The prefix exists so that a value is copied into the browser bundle. It works as designed.
  • A key was pasted into a file while prototyping, the file was committed, and the key was later removed from the file but not from the history.
  • An environment file was committed because the ignore file was created after the first commit.

The weakness has a name, CWE-798, use of hard-coded credentials, and it is common enough that one secrets detection vendor’s annual report counted 23.8 million secrets leaked in public repositories on a single code host in 2024, a 25 percent rise on the year before.

Which keys are allowed in the browser?

Some keys are designed to be public. A payment provider’s publishable key, a maps key restricted to your domain, or the public key a hosted backend gives your frontend are all meant to ship. They identify your project; they do not grant power on their own.

The test for the rest is what the key can do if a stranger holds it. If it can spend money (an AI model API, an email or SMS sender), read private data, write to your database without rules, or administer anything, it is a secret. When in doubt, read the provider’s own description of the key: most label secret keys as secret.

Remember the special case from securing a browser-built app: a public database key is only safe if the database rules are written. The key is not the lock.

How do you check your own app?

Ten minutes, no tools beyond the browser and your repository:

  • Open your live app, open the developer tools, and search the loaded JavaScript for words like key, secret, token and the prefixes your providers use for secret keys.
  • In the network tab, use the app and watch for requests that go straight from the browser to a paid third party API with an authorization header. That header is readable by every user.
  • In the repository, search the current code for the same patterns, and list every environment variable that carries the public prefix. Each one should be something you are happy to publish.
  • Search the history, not just the current files. A key deleted last month is still in every clone of the repository.

Your coding tool can do the reading. Try: “Search this repository, including the built frontend and environment files, for API keys, tokens and credentials. For each one, tell me whether it reaches the browser, what service it belongs to, and whether that service treats it as secret. Do not change anything yet.”

You found one. What now?

Rotate first. Deleting the key from your code does nothing for the copies that already exist in bundles, caches, clones and anyone’s notes. The same annual report found that 70 percent of the secrets it detected leaking in 2022 were still active, which is what happens when people delete instead of rotate.

The order that works:

  • Create a new key at the provider and revoke the old one. Check the provider’s usage page for activity you do not recognise, and note the date.
  • Move the code that needs the key to your server, and store the new key only in server side environment variables.
  • Deploy, then confirm the old key no longer appears in the live bundle.
  • Decide whether to rewrite the repository history. Once the key is revoked, history rewriting is tidiness rather than safety, and it is disruptive, so do it deliberately.

The OWASP Secrets Management Cheat Sheet puts the remediation goal simply: a new secret “must be able to be quickly created and implemented”. If rotating a key takes you a day of hunting, that is a finding in itself. The guide rotate a leaked secret, then clean up has the step by step version.

How do you call a paid API without exposing the key?

Put a small server route between the browser and the provider. The browser calls your route; your route checks who is calling, then calls the provider with the key it holds.

Done carelessly, that route becomes an open relay that spends your money for anyone who finds it. Done properly, it does four things: it requires a signed in user, it sends only the request shapes your feature needs rather than forwarding whatever the browser sends, it limits how often one user can call it, and it caps what one user can cost you. Those limits are what turn “we moved the key to the server” into “a stranger cannot run up our bill”.

How do you stop it happening again?

  • Add the environment files to the ignore file before the first commit, and keep an example file with names but no values.
  • Turn on secret detection where you commit. The OWASP cheat sheet recommends detecting secrets at the developer level, in the editor or with a pre-commit hook, before they are ever committed, and most code hosts can block a push that contains a known key format.
  • Give each key the least power that works: restricted keys, per environment keys, and spending limits at the provider.
  • Ask your coding tool, as part of every feature that touches a third party, where the key lives and whether it reaches the browser.

Is a key inside a mobile app or a desktop build any safer?

No. A compiled app is a file on the user’s device, and pulling strings out of it is routine. Obfuscation slows a casual reader down; it does not stop anyone who wants the key. The same rule applies as in the browser: anything you ship to a device, whether a web bundle, a mobile package or a desktop installer, is public.

The same goes for keys handed to the browser at run time. Fetching a secret key from your server and then using it in the browser is still putting it in the browser, just one request later. If the browser holds the key, the user holds the key. The only pattern that keeps a secret secret is the one above: the browser asks your server, and your server, which never sends the key anywhere, makes the call.

What is the one thing to do today?

Search your live bundle for secret keys. It takes ten minutes and it is the check with the most direct cost if you skip it. Then work through the secrets section of the free Vibe Coding Security Checklist, which ranks each secrets check by severity with a fix deadline and says how to check it by hand.

Sources

  1. Secrets Management Cheat Sheet, OWASP Cheat Sheet Series
  2. CWE-798: Use of Hard-coded Credentials, MITRE CWE
  3. Report: The State of Secrets Sprawl 2025 (GitGuardian), Help Net Security