Short answer: no. In a Vite app, every environment variable that starts with VITE_ is written into your JavaScript bundle at build time. That bundle is downloaded by every visitor, so anyone can read the value in DevTools.
The same is true of NEXT_PUBLIC_ in Next.js and REACT_APP_ in Create React App.
Vite hides variables without the VITE_ prefix from your frontend code on purpose. The prefix is how you tell Vite "this value is safe to publish".
Here's how secret keys usually end up public in AI-built apps:
import.meta.env.OPENAI_API_KEY. undefined in the browser, so the feature breaks.VITE_OPENAI_API_KEY.
Lovable, Bolt and most Cursor-built frontends are Vite apps, so this comes up a lot.
VITE_ variable
sb_publishable_) key, as long as Row Level Security is on for every table pk_live_, pk_test_) phc_, Sentry DSNs) sk-proj-), Anthropic ( sk-ant-), Groq, Replicate sk_live_) and webhook secrets ( whsec_) sb_secret_ key, which bypasses RLS entirelyre_, SendGrid SG.), AWS keys, database URLs with a password
Not sure which kind a key is? I made a free key checker that tells you what a key is and whether it's allowed in the browser. It runs entirely client-side, so the key never leaves the page.
supabase secrets set OPENAI_API_KEY=....
A prompt that works in Lovable, Bolt or Cursor:
Move every call that uses a secret API key out of the frontend into a
Supabase Edge Function that reads the key from Supabase secrets. Remove
the VITE_ variable for it and call the function instead. Only allow
logged-in users to call it. Don't print any keys in your reply.
Your .env file isn't what matters. What matters is what ended up in the built JavaScript. Two ways to check:
sk-, sk_live, service_role and .env files, source maps and missing headers.
The full version of this guide, with a FAQ, is here.