Your VITE_ API key is public. Here's what happens next, and three ways to fix it A developer is warning that API keys stored in frontend environment variables such as VITE_, NEXT_PUBLIC_ and REACT_APP_ are baked into the public JavaScript bundle and can be scraped by automated scanners, citing public reports of roughly $6,000 in unauthorized Gemini API charges within about 40 minutes and another $2,200 from a leaked VITE_GEMINI_API_KEY. The writeup outlines three mitigations: a serverless or edge backend endpoint holding the key as a secret, provider client-side options like Firebase App Check with Firebase AI Logic, and an edge proxy with per-token rate limits, spend caps and revocation. The developer notes that any browser-visible token can still be copied and replayed, so the goal is bounding the damage rather than preventing theft. If your frontend reads an AI or payments key from import.meta.env.VITE SOMETHING , that key is in the JavaScript bundle every visitor downloads. Same for NEXT PUBLIC in Next.js and REACT APP in Create React App. Putting it in a .env file changes where it's stored on your laptop, not where it ends up. You can check in ten seconds: open your deployed site, open DevTools → Sources, and search the bundle for your key's prefix sk- , AIza , sk live . Automated scanners crawl deployed sites and public repos for those prefixes. Developers have described the results publicly this year on Google's own forum, for example about $6,000 in roughly 40 minutes https://discuss.ai.google.dev/t/6-000-in-unauthorized-gemini-api-charges-from-a-vite-built-client-bundle-requesting-billing-review/142877 from an AI Studio sample exported to Vite, and about $2,200 https://discuss.ai.google.dev/t/urgent-2200-in-unauthorized-gemini-api-charges-from-leaked-client-side-key-requesting-refund/141634 from a VITE GEMINI API KEY used as a shared fallback. Providers have added protections Google now rejects unrestricted Gemini keys and offers project spend caps; OpenAI has project limits , but those work per project or account, not per key, and they don't stop the key from being copied. 1. A small backend endpoint. A serverless or edge function holds the key as a secret; your frontend calls the function. Free, and the right answer if you'll also add rate limiting and per-user budgets. 2. The provider's own client-side option, if it has one. For example, Firebase App Check with Firebase AI Logic for Gemini on mobile and web. 3. An edge proxy with per-key limits. You store the real key once and your app sends a stand-in token instead. The proxy adds the real key and forwards the call, and each token carries its own rate limit, allowed endpoints, spend cap and an off switch. I build one of these, KeyVault Edge https://keyvaultedge.com/?utm source=devto&utm medium=social&utm campaign=vite-key-public . You can watch it allow one request and block two misuse attempts on a live demo, no signup https://keyvaultedge.com/demo?utm source=devto&utm medium=social&utm campaign=vite-key-public . One honest limit for option 3 and for any token a browser can see : the token can still be copied and replayed from a server. The point is that a leaked token has hard limits and can be revoked in one click without rotating the provider key, so the damage is bounded. VITE , NEXT PUBLIC or REACT APP variable How did your key end up in the frontend? I'd like to hear what made it the easiest option at the time.