Your AI just put a secret key in the browser.
Find what your deployed app is actually shipping.
KeyDrift reads the JavaScript your app actually serves and finds the Supabase, Stripe, OpenAI and AWS keys that should never have left your server. Free, no account, and it knows the difference between a key that leaked and one that belongs there.
Sample result
acme-store.vercel.app
Supabase service_role key
_next/static/chunks/847.js:1 · eyJhbGci...d91a
Try a sample scan+
Public assets only
Fetches deployed JavaScript, never your private infrastructure.
No credentials required
Start with a URL or paste a bundle directly into the scanner.
Live keys never stored
Findings are masked and fingerprinted instead of retaining secrets.
Free first scan
No account is needed to find out what your app is shipping.
21
secret detectors
3
public credential formats, recognised so they are never reported as leaks
0
live keys stored, ever — findings carry a masked prefix and a fingerprint
Most scanners read your repository. The key was never in your repository.
The build is what leaks
NEXT_PUBLIC_ and VITE_ substitute the literal value into the bundle at build time. Your .env is correctly git-ignored and the key still ships to every visitor. A repository scanner sees nothing wrong because nothing is wrong — in the repository.
Knowing what is supposed to be there
A client bundle is meant to contain a Supabase anon key, a Stripe publishable key and a Firebase web key. A scanner that flags those has told you your working app is on fire. KeyDrift recognises them and excludes them — which is also what lets it say, with confidence, that the JWT next to them is a service_role key.
Why your tool did this
Lovable
Lovable builds a Vite single-page app with no server of its own. When you ask it to call an API that needs a key, the only place it can put that key is the browser — so it adds a `VITE_`-prefixed variable, and Vite substitutes the literal value into the bundle at build time.
Fix guides →Bolt.new
Bolt scaffolds Vite projects inside a WebContainer, where everything runs in the browser by definition. Code that works in the preview keeps the key client-side when it is deployed, because nothing in the generated project ever moved it to a server.
Fix guides →Cursor
Cursor edits the file you have open. Ask it to "call the OpenAI API" from a component and it writes exactly that — a client component holding a key — because the request named a file, not an architecture. The agent has no way to know the module is bundled for the browser.
Fix guides →Claude Code
A CLI agent working across the repo will follow the pattern it already sees. If one component reads a key from `import.meta.env`, the next feature it writes will too — the leak spreads by imitation rather than by a single mistake.
Fix guides →Replit
Replit Secrets are injected as environment variables at build time. A secret that gets a `VITE_` prefix so the frontend can "see" it stops being a secret — the prefix is the mechanism that copies it into the bundle.
Fix guides →Next.js
A server component can read `process.env.STRIPE_SECRET_KEY` safely. Move that same line into a client component and the build fails to find it, so the quickest fix — rename it with `NEXT_PUBLIC_` — is also the one that inlines it into the JavaScript every visitor downloads.
Fix guides →Rotating fixes today. Monitoring catches tomorrow.
The prompt that wrote this code is still in your editor, and it will write it again. Point KeyDrift at a deploy and it re-scans on every push, alerting only when something actually changes.
$19/mo Indie · $39/mo Team. Findings are never withheld on any plan.