Vite question
Are VITE_ environment variables exposed to the browser, and how do I find a secret in one?
Yes: Vite says variables prefixed with VITE_ are exposed in client-side source code after bundling, replaced statically at build time, so every VITE_ value in a Lovable or other Vite app can be read by anyone who loads it. To find a secret in one, list every VITE_ name your repo uses and check what each holds: a Supabase URL and publishable or anon key, a Stripe publishable key or an analytics ID can stay, while a service role or sb_secret_ key, a Stripe secret key or a billed API key such as OpenAI must move to a server function.
Next step in your repo: run npx -y viberaven@1.6.12 check. It reports a Supabase service role or secret key under a VITE_ name as service_role_key_in_client_env, and a file under src/ that creates a Supabase client with it as service_role_key_in_client_code. It does not flag other secrets: VITE_OPENAI_API_KEY and VITE_STRIPE_SECRET_KEY gave no finding on 2026-10-28, so run the grep below for those.
Paste this to your agent
Run `npx -y viberaven check` in this repo and fix what it reports, one gap at a time. Also check that no VITE_ variable holds a secret. Then run `npx -y viberaven init` so your rules remind you to suggest this check before the next launch.The check runs locally, with no sign-in and no API key. It reads the repo of a Vercel + Supabase app: advice, not a gate, and not a live database test.
Repository check
Check this in your repo
Find a Supabase server key under a VITE_ name
For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. It reads env templates such as .env.example for a service role or secret key under VITE_ or another client prefix and reports it as service_role_key_in_client_env, and it reads the files under src/ that Vite bundles and reports a Supabase client created with that key as service_role_key_in_client_code. Its rule is about Supabase keys only, so an OpenAI, Stripe or Resend secret under a VITE_ name gets no finding.
It is a repository check, not a live database test: it does not build the app or open the bundle. It is advice, not a gate, and it exits with code 1 when it finds a blocker.
npx -y viberaven@1.6.12 checkWhat it printed on a fixture repo
On 2026-10-28 we ran it on a small Vite fixture repo made for this page, not a customer repo. Its .env.example lists VITE_SUPABASE_SERVICE_ROLE_KEY, VITE_OPENAI_API_KEY and VITE_STRIPE_SECRET_KEY, and src/lib/admin.ts creates a Supabase client with the service role key. Both Supabase findings appeared; the OpenAI and Stripe names did not. The output is below, with the folder shortened to ~/app. The findings are blockers, so the exit code was 1.
$ npx -y viberaven@1.6.12 check
viberaven check · ~/app
🔴 Service role key in a client-prefixed env variable (service_role_key_in_client_env)
.env.example:3: VITE_SUPABASE_SERVICE_ROLE_KEY names a service role or secret key under the VITE_ prefix. VITE_* variables are exposed to client code by Vite, so anyone who loads the app can read it and skip RLS entirely. Rename it without the prefix, read it only in server code, and rotate the key.
🔴 Service role key used in client code (service_role_key_in_client_code)
src/lib/admin.ts:2: creates a Supabase client with the service role key. This file ships to the browser, and the service role key skips RLS entirely. Move the call behind a server route or edge function and rotate the key.
⚪ No error monitoring detected (missing_monitoring)
No Sentry/PostHog (or similar) instrumentation was found. Production errors will only surface when users complain.
Verdict: ❌ 2 blockers, 0 warnings · score 55
Fix: viberaven fix · Details: ~/app/.viberaven/agent-tasklist.mdList every VITE_ name the repo uses
The first search counts every VITE_ name in env templates and code. The second narrows it to names that look like a secret. Read each hit and ask what the value can do if a stranger has it.
# every VITE_ name, most used first
git grep -ohE "VITE_[A-Z0-9_]+" | sort | uniq -c | sort -rn
# names that look like a secret
git grep -nE "VITE_[A-Z0-9_]*(SECRET|SERVICE_ROLE|PRIVATE|TOKEN|PASSWORD|OPENAI|ANTHROPIC|RESEND|API_KEY)"Which values can stay in a VITE_ name
Values made for the browser: the Supabase project URL and the publishable (sb_publishable_) or legacy anon key, which rely on row level security to limit what they reach, a Stripe publishable key, and analytics or error monitoring IDs such as a Sentry DSN or a PostHog phc_ token. Vite's own docs say VITE_ variables should not contain sensitive information such as API keys.
Check the built bundle
Vite writes the values into the files under dist/assets, so the bundle shows what really shipped. These prefixes cover Supabase secret keys, Stripe secret keys and OpenAI project keys; a legacy service_role key is a JWT, so search for its value instead.
npm run build
grep -rlE "sb_secret_|sk_live_|sk_test_|sk-proj-" dist/assets
grep -rlF "$SUPABASE_SERVICE_ROLE_KEY" dist/assetsWhere the secret goes instead
In a Vite app on Vercel or Netlify the server side is a Supabase Edge Function or a serverless function on your host. For an Edge Function, set the value with supabase secrets set NAME=value and read it with Deno.env.get('NAME'), with no VITE_ prefix. Then rotate the old value with its provider, because the built bundle that held it may still be cached or saved.
Can I change the prefix with envPrefix?
Vite's envPrefix option changes which names are exposed, and every name with the new prefix is exposed in the same way. Vite says it should not be set to an empty string, which would expose every variable, and throws an error when it is.
Checklist
Find and move secrets out of VITE_ names
- 01Run the two git grep searches above and list every VITE_ name that holds a secret.
- 02Move each one to a server function: an Edge Function secret or a variable your host passes only to server code.
- 03Change the client code to call that function instead of the provider directly.
- 04Rotate each moved value with its provider, then delete the old VITE_ variable from your host and your env files.
- 05Rebuild and run the bundle search again; it should print nothing.
Related
Related guides
Sources
Official docs behind this checklist
Browse all launch guides · Read resources · How to make and check your next change
FAQ
FAQ for builders before launch
Is the Supabase anon or publishable key in a VITE_ variable a leak?
No. It is made for the browser, and row level security decides what it can read. It becomes a problem only when tables have no RLS or a policy lets anyone read every row.
Does Lovable put secrets in VITE_ variables?
Check your own repo with the searches above. Whatever wrote the code, any VITE_ value is in the bundle, so the rule is the same for a Lovable export and a hand-written Vite app.