0000 · 0000

Dappasol / Guides

Updated June 2026

Exposed API Keys in AI-Built Apps

AI coding tools leak secrets by slapping NEXT_PUBLIC_ or VITE_ on your keys, which bakes them straight into the JavaScript bundle every visitor downloads. A 2026 scan of roughly 380,000 AI-built apps found about 5,000 leaking sensitive data. Grep your repo for these prefixes, move every secret server-side, and rotate any key that ever touched git history.

Lovable, Bolt, Cursor, Replit, v0: they turn an idea into a running app overnight, and they are genuinely good at it. The trouble is what they do with your secrets. To make a Stripe call or an OpenAI request “just work” from the browser, they take the path of least resistance: a client-side environment variable. That one move can hand your billing key, your database credentials, or your third-party tokens to anyone who opens DevTools. Here is exactly how the leak happens, how to find it in your own app in about ten minutes, and how to shut it for good.

How AI tools leak your keys (the NEXT_PUBLIC trap)

Every modern framework splits environment variables into two worlds: server-only and client-exposed. A prefix decides which one you get. In Next.js, anything starting with NEXT_PUBLIC_ gets inlined into the JavaScript that ships to the browser. In Vite, the prefix is VITE_. In Create React App, it was REACT_APP_. Drop the prefix and the variable stays on the server, where the client never sees it.

Here is the trap. An AI tool writes a component that calls Stripe or OpenAI straight from the frontend, the build complains the variable is undefined in the browser, and the quickest way to kill that error is to add the public prefix. So the AI adds it. The app starts working, the demo looks great, and your secret key is now sitting in plain text inside main.[hash].js, free to download for every visitor who shows up.

The 380,000-app wake-up call

This is not theoretical. In a 2026 scan reported by Axios, the security firm RedAccess found roughly 380,000 publicly accessible assets built with AI “vibe coding” tools, and about 5,000 of them were leaking sensitive corporate or personal data. Exposed credentials and private information, sitting in the open, reachable by anyone who knew where to look. The same scan got picked up and corroborated across the security press.

The pattern is the real story, not the headline number. None of these were exotic zero-days. They were ordinary apps shipped fast, with secrets the builder never knew were public, because the tool made the insecure path feel like the working path. If you built something with an AI tool and wired in a real API key, you are in the population that scan was counting.

Find your exposed secrets in 10 minutes

You do not need a security team for this. Run these three passes in order, fastest to most thorough.

  1. Grep the source for public prefixes. From your project root, search for NEXT_PUBLIC_, VITE_, and REACT_APP_. For every hit, ask one question: is this value safe for the public to see? A publishable key is fine. A secret key, database URL, service-role token, or any private API key is a leak. One-liner: grep -rnE "NEXT_PUBLIC_|VITE_|REACT_APP_" src/.
  2. Inspect the built bundle. The source can look clean while the build still ships a secret. Run your production build, then search the output for telltale key shapes: grep -rE "sk_live|sk_test|AKIA|AIza|xoxb-" dist/ build/ .next/. Or load the deployed app, open DevTools, and search the loaded JavaScript for sk_ or your provider’s prefix. If a secret turns up here, every visitor already has it.
  3. Scan git history with a secrets scanner. Deleting a key from the current code does nothing if it is still sitting in an old commit. Run TruffleHog or gitleaks across the whole history: trufflehog git file://. or gitleaks detect --source .. These tools verify many keys against live APIs, so they tell you not just what leaked but what is still active.

Rotate and remediate

Find an exposed secret and deleting it from the code is the easy part, and the least important part. The key is already out. Anyone who downloaded your bundle or cloned your repo still has it. The only thing that actually closes the hole is rotation: generate a new key in the provider’s dashboard, swap it in server-side, and revoke the old one so the leaked value stops working.

Git history is where people get caught twice. Even after you remove a secret and commit the fix, the original value is still sitting in every prior commit, in every clone, in every fork. That is why “I already deleted it” means nothing. Any key that ever touched a commit is compromised: rotate it, revoke it. If you need the value gone from history entirely, say before you open-source, rewrite history with git filter-repo or the BFG. But rotation is the thing that protects you regardless.

PatternRiskFix
NEXT_PUBLIC_ / VITE_ prefixed secretInlined into the browser bundle and readable by every visitorDrop the public prefix, read the value only in server code, rotate the key
API key in a committed .env fileLives in git history forever; reachable in every clone and forkRemove from tracking, add .env to .gitignore, rotate the key, scan history
Secret used in a client-side fetch callKey travels to the browser to build the request; visible in source and network tabMove the call behind a server route or API endpoint that holds the key
Hardcoded token in source codeCommitted in plain text; leaks the moment the repo is shared or scannedReplace with a server-side environment variable, rotate the token

Server-side patterns that fix it for good

The durable fix is architectural. The browser should never hold a secret, so route every privileged call through your own server. The client asks your backend, your backend holds the key and talks to the third party, and the secret stays on the machine you control.

This is the same class of problem we cover in our guide on AI-generated code security risks, and it is one of the first things we check in our full audit of AI-generated code. Built on Replit specifically? See is Replit safe for the platform-specific gotchas.

Want us to run this audit for you?

We do a free 15-minute build audit: you show us your AI-built app, we tell you the specific security and production gaps and what it takes to fix them. No obligation.

Book your free build audit

FAQ

Why does my AI-built app leak API keys?

AI coding tools make a secret "work" in the browser by prefixing it with NEXT_PUBLIC_ or VITE_, which inlines the value straight into the JavaScript bundle every visitor downloads. Drop the public prefix, read the key only in server code, and rotate any key that was ever exposed.

How do I check if my API keys are exposed?

Run three passes. Grep your source for NEXT_PUBLIC_, VITE_, and REACT_APP_ prefixes. Build the app and search the output bundle for key shapes like sk_live or AIza. Then scan your full git history with TruffleHog or gitleaks. If a secret shows up in the bundle or history, treat it as public.

Do I need to rotate a key after I delete it from my code?

Yes. Deleting a key does not un-publish it. Anyone who downloaded your bundle or cloned your repo still has the value, and git history keeps it in every prior commit. The only reliable fix is to generate a new key, swap it in server-side, and revoke the old one.

Where should API keys live in a web app?

Secret keys belong only on the server, in environment variables without a public prefix, ideally in your host's encrypted settings. The browser calls your own server endpoint, and the server makes the privileged request. Only publishable or anon keys are safe to ship to the client.

Book a free 15-min build audit →