Your Gemini API Key Isn’t Safe in the Browser — Here’s What Actually Stops the Bill

Open the dev tools in any browser, click the Network tab, and load a page that calls an AI feature directly from client code. If the developer embedded an API key to make that call, it is sitting right there in plain text — no hacking required, just a right-click. For years this kind of mistake was mostly an embarrassment. With Gemini, it can now show up on an invoice.

Developer reviewing browser network traffic and Firebase security settings for Gemini API key protection

That shift is the real story behind a growing conversation among web developers about how to wire generative AI into apps safely. The technical fix — a managed proxy plus an attestation check — is not complicated once you understand what each piece is actually doing. The confusion, and the risk, comes from developers assuming one of those pieces does the other’s job.

The instinct that gets people in trouble

Adding an AI feature to a web app feels like it should be simple: install the Gemini SDK, pass in a key, call generateContent(), done. That pattern works great in a tutorial. The problem is that anything shipped to a browser is, by definition, public. A JavaScript bundle is just a file the browser downloads and runs — and so can anyone else, at any time, from anywhere. Pulling a key out of a production bundle is not a sophisticated attack; it is closer to reading a label on a box.

That has always been true of API keys in general. What has changed is the cost model. A blocked database read costs a company nothing. A Gemini call is billed per token, so an attacker who finds a live key does not just get "access" — they get a metered resource they can run in a loop. One documented case put real numbers on that gap: a small three-person startup with roughly $180 a month in normal cloud spend saw an exposed key rack up more than $82,000 in unauthorized Gemini charges over a single 48-hour window, after a key that had originally been scoped for Google Maps quietly gained access to Gemini once Google enabled the Generative Language API on the same project. That specific incident traces back to a scope-creep problem rather than a typical "forgot to hide the key" mistake, and it does not mean every leaked key leads to a bill anywhere near that size. But it illustrates the underlying shift well: a leaked AI key is not just a secrecy failure anymore, it is a live billing exposure the moment someone finds it.

Hiding the key is only half the job

The standard fix for "don’t put secrets in the browser" has always been a proxy: a small server sits between your app and the API, holds the real key, and forwards requests. Firebase AI Logic packages that pattern as a managed service, so instead of building and hosting your own proxy, you point your client SDK at Firebase’s infrastructure, which injects the key server-side before forwarding the call to Gemini.

That solves the leak problem — the key never reaches the browser. It does not solve a second, less obvious problem. Your app’s public Firebase configuration (project ID, app ID, and so on) is meant to be public; it ships in every bundle by design. Anyone who copies it can initialize their own client pointed at your Firebase project and call your proxy directly, with no real user behind the request at all. The key is safe, but the tap is still running for anyone who finds it.

This is the gap Firebase App Check is built for, and it’s the piece most tutorials gloss over. App Check is attestation, not authentication: it does not ask "who is this user?" — that’s what Firebase Authentication does — it asks "did this request come from a genuine, untampered instance of my app, or from a script pretending to be one?". On the web, that check typically runs through reCAPTCHA Enterprise, which performs an invisible risk assessment before issuing a short-lived signed token; the proxy checks that token before it will forward a request to the model. Google’s own documentation is careful to frame this correctly: App Check "prevents some, but not all, abuse vectors," and using it "does not guarantee the elimination of all abuse" — it’s one layer, not a finish line.

flowchart TD
A[Browser app code] --> B[App Check attestation check]
B -->|No valid token| C[Request rejected]
B -->|Valid token| D[Firebase AI Logic proxy]
D --> E[Gemini key injected server-side]
E --> F[Response returned to app]

Two locks, two different jobs

The reason people conflate hiding a key with blocking abuse is that both feel like "security," but they answer different questions. Laid out side by side, the roles stop overlapping:

Layer What it protects What it does not protect against
Raw key in client code Nothing — the key is readable by anyone who loads the page or inspects network traffic Any use, legitimate or not
Proxy (e.g. Firebase AI Logic) Keeps the real key off the client entirely, so it can’t be extracted from the bundle Anyone with your public app config can still call the proxy directly
App Check attestation Confirms the request came from a genuine, unmodified instance of your app, not a bot or copied config Doesn’t identify which human is making the request
Firebase Authentication Confirms which signed-in user is making a request Doesn’t stop a scripted client from hitting the proxy with no user context at all

Read across a row and the logic holds: a proxy without attestation still leaves an open, billable door for anyone who copies your public config. Attestation without a proxy has nothing to protect, because the key would already be exposed. The two are complementary, not redundant — which is also why Firebase has said App Check enforcement for AI Logic is set to become mandatory in late 2026, with the console’s guided setup already turning it on by default for new integrations well before that. Treat that timeline as a currently published schedule rather than a permanent fact, since AI product rollout dates have a habit of shifting.

Where the responsibility still sits with you

None of this makes the risk disappear, and it shouldn’t be read that way. App Check reduces unauthorized app instances from spending your quota; it does nothing about a legitimate, authenticated user who abuses a feature at volume, and Google is explicit that it blocks "some, but not all" abuse. Rate limits, budget alerts, and monitoring for unusual traffic are still worth having regardless of which proxy you use — Firebase AI Logic itself defaults to a per-user request cap, which is a sensible backstop rather than a replacement for watching your own usage. A managed proxy also doesn’t remove every reason you might want backend logic of your own — prompt templates, custom validation, or business rules can still belong on a server you control, even once the key itself is no longer your problem.

The underlying lesson generalizes past Gemini. Any secret that reaches a browser has effectively been published, whether it’s an AI key, a payment token, or an internal API credential. The fix has never really been "obscure the secret harder" — it’s structural: keep secrets on infrastructure the client can’t read, and put a second check in front of that infrastructure to confirm the request is coming from somewhere you actually recognize. For AI features specifically, that second check matters more than it used to, simply because the meter is now running per request instead of sitting quietly in the background.

Sources

  1. Why You Should Never Embed Your Gemini API Key in Client Code (And How Firebase AI Logic Fixes It)
  2. Google Gemini API Key Exposure: What Vibe Coders Must Know
Scroll to Top