← All guides

How to remove leaked VITE_ secret keys

In Vite (used by Lovable, Bolt and many Cursor/Replit projects), any env var starting with VITE_ is pasted into your public JavaScript at build time. That is fine for VITE_SUPABASE_URL and the anon key. It is a disaster for VITE_SUPABASE_SERVICE_ROLE_KEY or VITE_STRIPE_SECRET_KEY.

1. Check your built bundle

npm run build
grep -rEo 'sk_(live|test)_[A-Za-z0-9]+' dist/ | head
grep -rEo 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' dist/ | head   # decode each JWT; "role":"service_role" = leaked

2. Rename secrets so they are server-only

- VITE_SUPABASE_SERVICE_ROLE_KEY=...
- VITE_STRIPE_SECRET_KEY=...
+ SUPABASE_SERVICE_ROLE_KEY=...
+ STRIPE_SECRET_KEY=...

3. Move secret-key calls to the server

The browser should only use the anon key plus the signed-in user’s session. Anything needing a secret (creating Stripe Checkout sessions, admin queries) belongs in a server route or Supabase Edge Function, e.g. POST /api/create-checkout-session.

4. Stop committing .env

git rm --cached .env && echo ".env" >> .gitignore

Commit a .env.example that documents which keys are safe for the browser.

5. Rotate. This step is not optional.

Removing a key from code does not un-leak it: it is in every copy of your old bundle and in git history. Roll the Stripe secret key in the Stripe dashboard and rotate the Supabase service-role key (JWT secret), then update your server environment.

Rather have it fixed and proven?

Our flat-fee Rescue fixes this and hands you a before/after verification report within 48h.

See pricing