How to Stop Exposing API Keys in Frontend JavaScript Code
I was testing an AI-generated login page when I opened DevTools and saw this in the Network tab:
"openaiApiKey": "sk-proj-xxxxxxxx..."I didn’t write that code — an AI coding tool did. And it happily embedded my OpenAI key directly into the frontend JavaScript bundle.
Why This Happens
AI coding tools generate what you ask for. If you prompt “build me a chat feature”, it generates a complete solution — often with the API key hardcoded in a frontend .js file or a .env that gets bundled into the browser. The tool doesn’t know the difference between frontend and backend unless you tell it.
The problem: browser DevTools shows every byte of your JavaScript. There’s no “obfuscation” that hides a string from a determined user. Tools that claim to “minify and protect” your keys are lying — any key in the browser is extractable.
// I found this in a generated fileconst openai = new OpenAI({ apiKey: 'sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', dangerouslyAllowBrowser: true,});The dangerouslyAllowBrowser: true flag isn’t there by accident. OpenAI’s SDK explicitly warns you not to use it in production. It exists only for prototyping.
The BFF Proxy Pattern
The fix is a Backend-For-Frontend (BFF) — a thin backend that holds the API key and proxies requests. The frontend talks to your backend, your backend talks to OpenAI.
async function generateContent(prompt) { const response = await fetch('/api/generate', { method: 'POST', body: JSON.stringify({ prompt }), }); return response.json();}// API key lives here, never reaches the browserconst openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY,});
app.post('/api/generate', async (req, res) => { const completion = await openai.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: req.body.prompt }], }); res.json({ content: completion.choices[0].message.content });});The .env file on your server stays on your server. No .env gets bundled into the frontend build.
Firebase and Supabase Are Different
I see this confusion often: people panic when they see apiKey: 'AIzaSy...' in their Firebase config. That’s fine. Firebase and Supabase “API keys” are public client identifiers. Security for these services comes from Security Rules (Firebase) or Row Level Security (Supabase), not from hiding the key.
const firebaseConfig = { apiKey: 'AIzaSy...', // public client identifier authDomain: 'your-app.firebaseapp.com', projectId: 'your-app',};This is the equivalent of a Stripe publishable key. Anyone can see it, but it can’t do anything dangerous on its own.
But never confuse this with admin/service account keys. A Firebase Admin SDK private key or a Supabase service_role key in your frontend is a disaster — it bypasses all security rules.
The Financial Risk
An exposed OpenAI key costs real money. A scraper finds your key in a bundle, calls GPT-4o thousands of times, and you get a bill for $2,000 before you notice.
- Frontend keys: never for AI APIs, never for database admin access
- Firebase/Supabase config keys: safe in frontend, enforce security via rules
- Service account keys: never in frontend, ever
How AI Coding Tools Fit In
Most AI coding tools don’t understand deployment boundaries. They see “JavaScript project” and emit a single codebase. When you ask for a feature that needs an API, you must explicitly say “create a backend endpoint for this, the frontend calls the backend”. Or scaffold your project with a BFF structure upfront.
If you’re using an AI tool, add this to your prompt:
“Generate a backend API route for OpenAI calls. The frontend must never contain an API key.”
In this post, I covered why AI-generated frontend code often leaks API keys, how to use the BFF proxy pattern to keep keys on the server, and which “API keys” (Firebase, Supabase) are actually safe to expose in the browser.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 OpenAI API key safety
- 👨💻 Firebase Security Rules
- 👨💻 Supabase Row Level Security
- 👨💻 Reddit Discussion: AI generated websites vulnerability check
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments