Is my API key exposed in my website’s JavaScript?
Written by our founder, a cybersecurity architect · Updated
Short answer
Open your live site, open the browser’s developer tools and search every loaded JavaScript file for the start of your key, like sk_live_ or sk-. If you can find it there, so can every visitor, and you should rotate the key today.
Keys get there because build tools copy some environment variables into the browser code on purpose: anything starting with VITE_ in Vite apps (which includes most apps built with Lovable and Bolt) or NEXT_PUBLIC_ in Next.js. A secret stored under one of those names is public, whatever your .env file looks like.
Not every visible key is a problem, because some are designed to be public. The lists below say which ones are safe and which ones need fixing.
Why do secret keys end up in the browser?
When your app is built, the build tool replaces every public environment variable with its value, right inside the JavaScript files your site serves. These prefixes are public:
VITE_in Vite apps, including most apps built with Lovable and Bolt.NEXT_PUBLIC_in Next.js.REACT_APP_in Create React App andEXPO_PUBLIC_in Expo.
Keys also leak when an AI tool writes them straight into a component to make a feature work, or adds them to the define option in vite.config. After the build, the variable name is gone and only the key is left, so searching your source code for VITE_ isn’t enough: you have to search what was built.
How do I search my live site for keys?
- Open your site in Chrome or Firefox, sign in if your app has accounts, and click through the main pages so their code loads. Code that only loads after sign-in is still downloaded by anyone who signs up.
- Open the developer tools (F12), go to the Sources tab (Debugger in Firefox) and press Ctrl+Shift+F (Cmd+Option+F on a Mac) to search every loaded file.
- Search for the prefixes of the keys you use:
sk_live_andrk_live_(Stripe),sk-(OpenAI and Anthropic),sb_secret_(Supabase),AKIA(AWS),ghp_(GitHub).
Supabase service_role keys are harder to spot, because they are long tokens starting with eyJ, just like the public anon key. Paste a token you find into a JWT decoder: if it says "role": "service_role", it is the secret one.
How do I check before I deploy?
Build the app locally and search the output. Vite writes it to dist/, Next.js to .next/static/:
npm run build
grep -rnoE "sk_live_[0-9a-zA-Z]+|rk_live_[0-9a-zA-Z]+|sk-[A-Za-z0-9_-]{20,}|sb_secret_[A-Za-z0-9_-]+|AKIA[0-9A-Z]{16}|ghp_[0-9a-zA-Z]{36}" dist/No output means none of those keys are in the build. The real fix is upstream, though: keep secrets in variables without a public prefix, and use them only in server code such as an API route, a Supabase Edge Function or a serverless function on your host.
Which keys are safe to be public?
- The Supabase anon or publishable key (
sb_publishable_…). It only gives what your Row Level Security policies allow, so check those with our Supabase RLS guide. - The Firebase web config (
apiKey: "AIza…"). It identifies your project, and your Firebase security rules decide what it can read. - Stripe publishable keys (
pk_live_…). - Browser keys for maps or analytics, ideally restricted to your domain in the provider’s console.
Everything else is a secret and must never reach the browser:
- Stripe secret and restricted keys (
sk_live_…,rk_live_…). - OpenAI (
sk-…,sk-proj-…) and Anthropic (sk-ant-…) keys. Strangers who find them run their own requests on your bill. - The Supabase
service_roleor secret key (sb_secret_…), which ignores Row Level Security. - AWS access keys (
AKIA…), GitHub tokens (ghp_…), email API keys (Resend, SendGrid), Slack webhook URLs and private keys.
I found a key. What now?
- Rotate it first: create a new key in the provider’s dashboard and revoke the old one. Removing the line from your code doesn’t help on its own, because old deployments, caches and your Git history still have it.
- Move the code that uses it to the server, and keep the new key in a variable without a public prefix.
- Check the provider’s usage and billing for calls you didn’t make, and set a spending limit where the provider offers one.
Bots search public GitHub repositories for keys all the time, so treat any key that was ever pushed to a public repository as used, even if you deleted it a minute later.
What about source maps?
Source maps (files ending in .map) let the browser show your original source code instead of the minified build. If they are published, anyone can read your components, comments and file names, which makes the rest of your app much easier to study.
Vite and Next.js don’t publish them by default (build.sourcemap in vite.config, productionBrowserSourceMaps in next.config). If .map files load from your live site, a setting or a plugin turned them on.
Can I check this automatically?
Our free scan downloads your page and its JavaScript the way a browser does and looks for the secret keys listed above, published source maps and files like .env left public. It takes about a minute with no account, masks any key it finds and never uses it. It only sees what loads without signing in. Connect your repository and our code scan, free for public repositories and 2 private ones a month, also looks for secrets on every pull request, before they are deployed.
Checklist
- No secret is in a
VITE_,NEXT_PUBLIC_or other public variable - The built output was searched for key prefixes before deploying
- Secrets are used only from server code or edge functions
- Exposed keys were rotated, not just deleted
- AI and cloud providers have spending limits
- Source maps are not published in production
- Public keys (Supabase anon, Firebase, Stripe
pk_) are backed by RLS or security rules
Scan your site for free
Leaked API keys, exposed files and missing security headers, with a fix for each. No account, about a minute.
Questions
- Is it safe to keep API keys in a .env file?
- Yes, as long as the variable has no public prefix like VITE_ or NEXT_PUBLIC_, the file is not committed to Git and it is not in a folder your site serves. The build copies only public variables into your JavaScript.
- Is my Supabase anon key a secret?
- No, it is meant to be in your frontend. It only grants what your Row Level Security policies allow, so check those with our Supabase RLS guide.
- Can I hide an API key in the frontend by encoding or obfuscating it?
- No. The browser has to turn it back into the real key to use it, so anyone can do the same. Secret keys belong on a server.
- Does deleting the key from my code make it safe again?
- No. Old deployments, caches and your Git history keep it. Rotate the key first, then remove it from the code.