All posts
5 min read

How to Keep API Keys Out of an AI-Built App

AI-generated code hardcodes API keys all the time — and 'put it in an environment variable' isn't enough if the variable ends up in the browser. Which keys are safe to expose, which never are, and how to fix a key that's already leaked.

securityno-codegetting starteddeployment

Ask an AI to "connect my app to OpenAI" or "add Stripe payments," and there's a good chance the first working version has your secret key pasted directly into the code. It works immediately, which is exactly why the AI did it.

It's also how keys get stolen. Bots continuously scan public GitHub repositories for credentials, and a leaked key can be found and abused within minutes of being pushed. For AI keys, that means someone else's usage on your bill. For payment keys, it can be much worse.

Most guides stop at "use environment variables." That advice is necessary and not sufficient, because in a modern web app, an environment variable can still end up in the browser. Here's the whole picture.

The one rule

Anything the browser can see, anyone can see.

Your frontend code is downloaded to every visitor's device. They can open developer tools and read it — including any key embedded in it, however you got it there. Minified, bundled, or split across files, it's still there.

So the real question for every key is: does this key need to be secret?

Public keys vs secret keys

Many services deliberately give you two kinds of key:

Service Safe in the browser Must stay on the server
Stripe Publishable key (pk_…) Secret key (sk_…), webhook secret
Supabase Anon / publishable key Service-role / secret key
Firebase Web config (API key, project ID) Admin SDK service account
OpenAI, Anthropic, most AI APIs Nothing The API key — always
Email providers (Resend, SendGrid…) Nothing The API key

The public keys are designed to be exposed. They only work together with rules on the provider's side — Supabase's row-level security, Firebase's security rules, Stripe's restriction to creating checkout sessions. The secret keys bypass those rules. A secret key in the browser means anyone can do anything your account can.

AI API keys have no public version. If your frontend calls an AI API directly with your key, your key is public.

The environment variable trap

"Put it in a .env file" keeps a key out of your git repository. Whether it stays out of the browser depends on the framework:

  • Vite exposes any variable starting with VITE_ to the browser.
  • Next.js exposes any variable starting with NEXT_PUBLIC_ to the browser.
  • Create React App exposed anything starting with REACT_APP_.

Those prefixes exist to mark "this is safe to be public." When an AI names a variable VITE_OPENAI_API_KEY to make an error go away, it has put your secret key into every visitor's browser — neatly, via an environment variable.

Check now: search your project for VITE_, NEXT_PUBLIC_, and REACT_APP_. Anything with one of those prefixes should be a value you're comfortable publishing.

The fix: a server in the middle

Secret keys must only be used by code that runs on a server. The browser asks your server to do the thing; your server uses the key and returns the result.

Browser  ──►  your API route  ──►  OpenAI / Stripe / email provider
               (holds the key)

In practice that's a small backend endpoint or a serverless function:

  • Next.js: an API route or server action. Variables without the NEXT_PUBLIC_ prefix are only available there.
  • Vite / React frontend: a serverless function on your host, or a small backend service.
  • Supabase: an Edge Function, with the secret stored in Supabase's secrets, not in the frontend.

When you ask the AI to do this, be explicit: "Move the OpenAI call to a server-side API route. The API key must never be sent to the browser."

Two things to add while you're there:

  • Authentication on the endpoint. Otherwise anyone can call your API route and spend your key's budget through it.
  • Rate limits and a spending cap. Set a monthly limit in the provider's dashboard so a bug or abuse has a ceiling.

Keep secrets out of git

  • .env in .gitignore, always. Commit a .env.example with fake values so others know which variables exist.
  • Secrets set in your host's dashboard, not in files that get deployed.
  • Check before your first push. git status should never show .env.
  • Enable secret scanning on your GitHub repository. It will warn you about known key formats.

Remember that git history is forever. Deleting a key from the code in a later commit doesn't remove it from earlier commits.

If a key has already leaked

Deleting it from the code does not fix anything. Assume it's been copied.

  1. Revoke or rotate the key in the provider's dashboard, immediately. This is the only step that actually stops misuse.
  2. Create a new key and put it in your host's secret settings — not in the code.
  3. Check the provider's usage and billing for activity you don't recognise, and contact their support if there is some.
  4. Check logs for anything the key could reach: data read, emails sent, charges made.
  5. Fix the cause — move the call server-side, fix the variable prefix — so the new key doesn't leak the same way.

Rotation first. Everything else second.

A five-minute audit

  • Search the code for sk_, sk-, api_key, apiKey, secret, token.
  • Search for VITE_, NEXT_PUBLIC_, REACT_APP_ and check each value is genuinely public.
  • Open your live site, open developer tools, and search the loaded JavaScript for part of each secret key.
  • Confirm .env is in .gitignore and not in your git history.
  • Confirm AI and payment calls happen server-side, behind authentication.
  • Set spending limits with every paid API provider.

If the live-site search finds a secret key, treat it as leaked and rotate it.


EasySpawn keeps secrets in environment variables managed by the platform, separate from your code, with server-side code running alongside your app — so keys stay on the server where they belong. See how it works for AI-built apps or join the waitlist.

Related: A Security Checklist for Vibe-Coded Apps · You Built an App With AI. Now What?

Keep reading