Guides / Firebase API key in your frontend
Is it safe to expose your Firebase API key? What actually protects your app
You opened DevTools, saw apiKey sitting in your Firebase config, and wondered if you just leaked something. Usually you didn't. Firebase web API keys are meant to be in the browser. But "safe" depends on three things that have nothing to do with hiding the key, and one of them can quietly put your Google Cloud bill at risk.
The short answer
Yes, the Firebase apiKey in your web config is fine to ship, as long as all of these are true:
- The key is restricted to Firebase APIs only. Firebase's docs say these keys only identify your project and app. They don't authorize anything. A key with Don't restrict key selected, though, can call any API that's enabled in your project.
- Your Security Rules lock down your data. Firestore, Realtime Database and Cloud Storage are protected by rules, not by keeping the key secret.
- App Check covers the services you use, so requests from scripts that aren't your app get rejected once you enforce it.
The one key that must stay secret: anything that can call the Gemini Developer API (the Generative Language API). Firebase says it should never be in your code or config files, and never on the allowlist of a public key or a key used for other services. If your Firebase browser key can call Gemini, anyone who copies it from your site can run requests on your project.
How it happens
Firebase creates a Browser key (auto created by Firebase) when you create a project. Since May 2024, new auto-created keys come restricted to Firebase-related APIs. That still leaves a few common ways to end up exposed:
- You reused the browser key for Maps, Places or Gemini, so those APIs got added to its allowlist (or you removed the restrictions to make an error go away).
- You made your own key in the Google Cloud console. Keys Firebase didn't create weren't touched by the May 2024 change.
- An older Firebase key was unrestricted before May 2024. Firebase restricted those to the Firebase APIs plus any APIs already enabled in the project at the time, so it might still allow something you don't want.
- Your AI tool wired a Gemini call straight into the frontend with an API key, which puts that key in your bundle for anyone to read.
If Google Cloud emailed you about a "publicly accessible Google API key", this is the check it's asking you to do.
How to check
None of these commands print a key value.
1. See every key and what it's allowed to call
In the Google Cloud console, open APIs & Services > Credentials for your Firebase project. Or, with the gcloud CLI:
gcloud services api-keys list --project=YOUR_PROJECT_ID \ --format="yaml(displayName,uid,restrictions)"
This prints each key's name, ID and restrictions only. For each key, look at:
- No
restrictionsat all, or noapiTargets. That key has no API restrictions and can call any API enabled in the project. Fix this first. generativelanguage.googleapis.comin the list. That's the Gemini Developer API. It shouldn't be on any key that ships to a browser.- Non-Firebase APIs on the Firebase key, such as Maps, Places or other billable Google Cloud APIs. Firebase recommends a separate, restricted key for each of those.
To see which key your web app actually uses, go to the Firebase console, open Project settings > General, find your web app under Your apps, and match it against the key names above. By default it's the Browser key.
2. Test whether your public key can call Gemini
Paste the key from your web config when prompted. It isn't echoed or saved in your shell history, and curl prints only the status code:
read -rs FIREBASE_WEB_API_KEY
curl -s -o /dev/null -w '%{http_code}\n' \
"https://generativelanguage.googleapis.com/v1beta/models?key=$FIREBASE_WEB_API_KEY"
unset FIREBASE_WEB_API_KEY
403 is what you want. It means the key is blocked from that API, or the API isn't enabled in the project. 200 means anyone who copies the key from your site can use the Gemini API on your project. Go straight to the fix below.
3. Find frontend code that calls Google APIs directly
This lists file names only, so no inline keys get printed:
grep -rlE "generativelanguage\.googleapis|@google/genai|@google/generative-ai|maps\.googleapis" \ src app pages components lib 2>/dev/null
Any client-side file in that list is worth a look. Gemini calls should go through your server or Firebase AI Logic, not through a key in the browser.
4. Confirm rules and App Check
Your rules are what actually protect the data. If you haven't checked them, run through Firebase security rules still in test mode. Then open Security > App Check in the Firebase console to see whether your apps are registered and whether enforcement is on for each service.
How to fix it
Restrict the Firebase key to the Firebase APIs you use
In APIs & Services > Credentials, open the key, choose Restrict key under API restrictions, and keep only the Firebase-related APIs. With gcloud, this example is for an app that uses Authentication and Cloud Firestore:
gcloud services api-keys update KEY_ID --project=YOUR_PROJECT_ID \ --api-target=service=firebase.googleapis.com \ --api-target=service=logging.googleapis.com \ --api-target=service=identitytoolkit.googleapis.com \ --api-target=service=securetoken.googleapis.com \ --api-target=service=firestore.googleapis.com \ --api-target=service=datastore.googleapis.com
Without --append, this replaces the key's current allowlist, so match it to the products you actually use. Firebase's API key page has the full table: Cloud Storage needs firebasestorage.googleapis.com, Cloud Messaging needs fcmregistrations.googleapis.com and firebaseinstallations.googleapis.com, and so on. By default gcloud checks the last 7 days of traffic and refuses a change that would block it. Firebase also suggests trying new restrictions on a test key before touching your production key. If something breaks with API_KEY_SERVICE_BLOCKED or a 403, an API you need is missing from the list.
Give Gemini and Maps their own keys
Create a separate key for each non-Firebase API and restrict it to that one API. The Gemini key goes on your server only. A minimal Next.js route handler:
// app/api/ask/route.ts — runs on the server, the key never reaches the browser
export async function POST(req: Request) {
const { prompt } = await req.json();
const res = await fetch(
"https://generativelanguage.googleapis.com/v1beta/models/YOUR_MODEL:generateContent",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-goog-api-key": process.env.GEMINI_API_KEY!, // no NEXT_PUBLIC_ prefix
},
body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] }),
}
);
return Response.json(await res.json(), { status: res.status });
}
Put your own checks in front of a route like this, such as signed-in users only or a rate limit, or it becomes a free Gemini proxy for anyone. If you'd rather call Gemini from the client, Firebase AI Logic is built for that and works with App Check.
If a key that could reach paid APIs was public, replace it
Restricting the key stops new abuse. If it could call Gemini or another billable API while it was public, also create a fresh restricted key, move your app to it, check billing for usage you don't recognize, and then delete the old one. Deleting a key your app still uses breaks it, and deleting is permanent, so switch over first. Find and rotate exposed API keys walks through the order.
Tighten the sign-in quota if you use email and password
Firebase points out that someone with your key can send authentication requests to your project. They still can't read data your rules protect. If you use password sign-in, you can lower the quota on the identitytoolkit.googleapis.com endpoints in the Google Cloud console to fit your normal traffic. Just raise it again if you suddenly get a lot of real users.
Turn on App Check
For web apps, create a score-based reCAPTCHA Enterprise key for your domains, register your app under Security > App Check, then initialize App Check before you use any other Firebase service:
import { initializeApp } from "firebase/app";
import { initializeAppCheck, ReCaptchaEnterpriseProvider } from "firebase/app-check";
const app = initializeApp(firebaseConfig);
initializeAppCheck(app, {
provider: new ReCaptchaEnterpriseProvider("YOUR_RECAPTCHA_ENTERPRISE_SITE_KEY"),
isTokenAutoRefreshEnabled: true,
});
Ship it, watch the App Check request metrics, and turn on enforcement for each service once real traffic shows as verified. App Check blocks some abuse, not all of it, so it works alongside your rules rather than replacing them.
next step
Check your live app for free
Our free Leak Checker looks at your deployed app the way any visitor's browser does and flags exposed secret keys, public source maps and missing security headers. It's a quick way to make sure the only key in your bundle is the one Firebase meant to be there.
If you'd rather have the whole app looked at, the $99 Scan + Report covers your repo and live app, including leaked keys, deploy settings, database access rules and auth checks.
Sources
Every product step above was checked against these official pages. Last checked Oct 2026. Products change; if a menu moved, the docs below are the source of truth.
- Firebase: Learn about using and managing API keys (restrictions, Gemini, quotas, required APIs)
- Firebase: App Check overview
- Firebase: App Check with reCAPTCHA Enterprise in web apps
- Firebase: Avoid insecure rules
- Google Cloud: gcloud services api-keys list
- Google Cloud: gcloud services api-keys update (--api-target, --append, existing-usage check)
- Gemini API: Models (models.list endpoint)
- Gemini API reference (x-goog-api-key header)