DEV Community

Milo Kits
Milo Kits

Posted on Originally published at milokits.com

Is your API key in your JavaScript? VITE_ and NEXT_PUBLIC_ explained

If your code reads an API key through a variable starting with VITE_, NEXT_PUBLIC_ or REACT_APP_, that key is in your public JavaScript and anyone can copy it. An environment variable keeps it out of your Git repo, not out of the browser: the build tool pastes the value into the bundle.

Why the prefix matters

  • Vite (Lovable, Bolt): VITE_ variables are exposed in client code, and the Vite docs say they shouldn't hold secrets.
  • Next.js (v0, many Cursor projects): NEXT_PUBLIC_ variables are inlined into the browser JavaScript at build time.
  • Create React App: same for REACT_APP_.

Hardcoding a key in a component is the same problem. If the browser runs the code, the browser has the key.

Safe in the browser vs secret

Usually fine in frontend code Never in frontend code
Supabase anon / sb_publishable_ key (with RLS) Supabase service_role / sb_secret_
Stripe pk_live_... Stripe sk_live_... or rk_...
Firebase web config Firebase Admin service account JSON
Map keys restricted by domain OpenAI, Anthropic, Gemini, Resend, Twilio, AWS keys, database URLs

Check your live site in two minutes

  1. Open your live site, then DevTools (F12) > Sources. Press Ctrl/Cmd+Shift+F to search all files.
  2. Search for: sk-, sk_live, rk_live, service_role, sb_secret_, AKIA, re_, SG., -----BEGIN.
  3. In the Network tab: if the browser calls api.openai.com directly, the key is in the browser.
  4. Locally, after npm run build, search dist/ or .next/ for the same strings.

The fix: call the API from the server

The browser calls your own endpoint (an API route, serverless function or Supabase Edge Function), and that endpoint calls the third-party API with a key from a server-only env var. Add a login check and a rate limit so it doesn't become a free proxy.

My [SERVICE] API key is exposed in my frontend (read from [VARIABLE] in [FILE]). Please: 1) create a server-side endpoint that reads the key from a server-only env var with no public prefix, 2) remove the key and the prefixed variable from all client code, 3) make the frontend call the new endpoint, require a logged-in user and add a per-user rate limit, 4) show me the diff and where to set the env var on my host.

Already leaked? In this order

  1. Rotate the key in the provider's dashboard.
  2. Set a spending limit or usage alert.
  3. Check the provider's usage logs.
  4. Deploy the server-side fix.
  5. If it was ever committed to Git, treat it as public even after deleting the file.

Free pre-launch checklist (14 checks to run before you share your link): https://milokits.com/checklist?ref=devto

We make Milo's Launch Check, a passive outside-in check for AI-built apps that also looks for keys in your bundle. Launch week: Quick Check $19 instead of $29 until 1 November (code QUICK19): https://milokits.com/?ref=devto#pricing

Top comments (0)