Are VITE_ environment variables public? Yes. How to keep API keys out of your Vite, Lovable or Bolt frontend
Short answer: yes. Anything in a VITE_ variable is baked into the JavaScript every visitor downloads, and the same goes for NEXT_PUBLIC_ and REACT_APP_. Publishable keys belong there; secret keys don't. To see what your own live site ships, run a free SniffPup scan (SniffPup is a free passive security scanner for vibe-coded apps). Only check sites you own.
Home › Guides › Are VITE_ variables public? · Last updated · By Ember, SniffPup
The fast way: a free SniffPup scan runs 15 checks from the outside, including a read of your page and up to 20 same-site JavaScript files for OpenAI, Anthropic, Stripe, Supabase, AWS, GitHub and other secret keys (masked in the report), plus public .env files and source maps. No signup. Ember sniffs the bundle so you don't have to squint at minified code.
A 100 means no issues were found in our checks. It is never proof of security.
1. Why VITE_ variables end up in the browser
Per Vite's env variables docs, only variables that start with VITE_ are exposed to your client code as import.meta.env.VITE_..., and Vite replaces them with their actual values when it builds. The prefix is the "yes, ship this to the browser" switch. Vite's docs warn that VITE_ variables should not contain sensitive information. Next.js does the same with NEXT_PUBLIC_ (its environment variables docs say these are inlined into the JavaScript sent to the browser), and Create React App with REACT_APP_.
Lovable and Bolt apps are usually Vite projects, so this is the most common way an AI-built app leaks a key: you ask for an OpenAI feature, the AI puts VITE_OPENAI_API_KEY in .env, and it works. It also works for everyone else who opens DevTools.
2. Sort your keys: public by design vs secret
- Fine in the frontend: Supabase publishable (
sb_publishable_) or anon key, Firebase web config, Stripe publishable key (pk_), and analytics or map keys restricted to your domain. These are meant to be public; their safety comes from rules elsewhere (for Supabase, Row Level Security). - Never in the frontend: OpenAI, Anthropic and other AI keys, Stripe secret keys (
sk_), Supabase secret (sb_secret_) orservice_rolekeys, AWS keys, database URLs with passwords, webhook signing secrets and email API keys.
Quick test: if the key's dashboard calls it "secret", or it can spend money or read every row, it doesn't get a VITE_ prefix.
3. Move the secret to a server function
Put the key where only server code can read it, and have your frontend call that code. Common homes for vibe-coded apps:
- Supabase Edge Function: store the key as a function secret and read it with
Deno.env.get("OPENAI_API_KEY"). Lovable's Secrets panel works this way for Supabase-backed apps. - Vercel or Netlify function: add the variable in the host's dashboard without a public prefix and read it in an API route.
The function should check who is calling (a signed-in user, a rate limit), call the provider with the key and return only the answer. A paste-ready prompt for your AI builder: "Move every secret API key out of VITE_ variables into a server-side function, keep only publishable keys in the frontend, and have the frontend call the function instead."
4. Already shipped a secret? Replace it
Deleting the variable isn't enough: old bundles get cached and crawled. Create a new key, put it in the server function, deploy, then revoke the old key and check the provider's usage page. The step-by-step version is in leaked API key: what to do, with provider-specific guides for OpenAI and Stripe.
5. Check what your live site actually ships
Open your live site, open DevTools, and search all loaded files for sk-, sk_live_, sb_secret_ and service_role. Or let the free frontend env variable leak checker or a full SniffPup scan do it. The exposed API keys guide covers the manual search in more detail.
FAQ
Are VITE_ environment variables public?
Yes. Vite only exposes variables that start with VITE_ to your client code, and it does that by writing their values into the JavaScript bundle at build time. Anyone who opens your site can read them in DevTools. Vite's own docs say VITE_ variables should not contain sensitive information.
Does putting my key in a .env file keep it secret?
Not by itself. The .env file stays on your machine or build server, but the moment you prefix a variable with VITE_, NEXT_PUBLIC_ or REACT_APP_ and use it in frontend code, its value ships to every visitor. A .env file only keeps a value private if frontend code never reads it.
Which keys are OK in a VITE_ variable?
Keys designed to be public: the Supabase publishable or anon key, a Firebase web config, a Stripe publishable key (pk_), and analytics or map keys locked to your domain. Never secret keys: OpenAI or Anthropic keys, Stripe secret keys (sk_), Supabase secret or service_role keys, AWS keys, database passwords or webhook secrets.
Where should a secret key go instead?
In a server-side function that your frontend calls: a Supabase Edge Function with the key stored as a function secret, a Vercel or Netlify serverless function, or your builder's Secrets panel used by backend code. The function checks the user, calls the API with the key and returns only the result.
My secret key was already in a VITE_ variable. Is deleting it enough?
No. Every visitor and every crawler that loaded the old bundle may have a copy, and old builds can stay cached. Create a new key in the provider's dashboard, move it to a server function, deploy, then revoke the old key and check the provider's usage page for calls you didn't make.
Does a clean SniffPup report mean my env variables are safe?
No. A score of 100 means no issues were found in our checks. It is never proof of security: the scan reads what your live site serves to a visitor, not your repo, your server code or pages behind a login.
Sources: Vite env variables and modes, Next.js environment variables and Supabase function secrets docs, checked October 29, 2026.
A score of 100 means no issues were found in our checks. It is not proof that your app is secure.