How to Fix Firebase API Key Exposure (2026)

A Firebase service account JSON file committed to a public GitHub repo gives any attacker full admin access to your Firestore database, the ability to create and delete users, and a path to your Cloud Storage buckets. We find this credential type in source repos more often than any other Firebase issue.

But there's a complication: Firebase has two completely different types of credentials, and most "Firebase API key exposure" guides confuse them. Fixing the wrong one wastes time and leaves the real risk open.

TL;DR

The apiKey in your Firebase web SDK config (AIzaSy...) is not a secret. It's a project identifier that Google publishes intentionally. Your data is protected by Security Rules, not by keeping this key private. What you must protect is the Firebase Admin SDK service account JSON file. If that file is in your git history or in client-side JavaScript, rotate it immediately. Also check that your Security Rules are not set to allow read, write: if true.

Two Credentials, Two Threat Models

Before rotating anything, identify which one leaked.

The Public Web API Key

Your Firebase project has a config object that goes in your frontend code:

const firebaseConfig = {
  apiKey: "AIzaSyD-9tSrke72I6e4",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project",
  storageBucket: "your-project.appspot.com",
  messagingSenderId: "123456789",
  appId: "1:123456789:web:abc123"
};

That apiKey starting with AIzaSy is visible in your deployed JavaScript and that's intentional. Google's documentation explicitly says this value is not secret. It tells the Firebase SDK which project to connect to. Your Security Rules determine whether any given operation succeeds. An attacker with only this key can read your database if and only if your rules permit unauthenticated reads.

If only the web apiKey is "exposed": You don't need to rotate it. Fix your Security Rules instead (see Step 4 below).

The Admin SDK Service Account Key

This is the dangerous one. It looks like a JSON file:

{
  "type": "service_account",
  "project_id": "your-project",
  "private_key_id": "abc123",
  "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowI...",
  "client_email": "firebase-adminsdk-xyz@your-project.iam.gserviceaccount.com",
  "client_id": "12345678901234567890"
}

This key bypasses every Security Rule. Anyone who has it can read, write, and delete everything in Firestore, Realtime Database, and Cloud Storage without authentication. It's also the credential that lets you create and delete Firebase Auth users.

If this file is in your git history or client-side code: Rotate immediately. This guide's Steps 2-3 cover that.

Never import firebase-admin in your React, Vue, or Vite app. The Admin SDK requires the service account credentials, and any import in client-side code will bundle those credentials into your JavaScript. Every visitor to your site can then extract them.

Step 1: Find What Actually Leaked

1

Check git history for the service account JSON file.

# Look for JSON files with service_account content ever committed
git log --all --full-history --name-only -- "*.json" | grep -E "\-adminsdk-|service-account|firebase-credentials"

# Search commit contents directly
git log --all -p | grep -A5 '"type": "service_account"' | head -20

If any commits return results, the key was in your repository and should be considered compromised.

2

Search your current codebase for Admin SDK credentials.

# Find imported service account files
grep -r "require.*firebase.*json\|import.*serviceAccount" --include="*.js" --include="*.ts" .

# Find environment variables that might hold the private key inline
grep -r "FIREBASE_ADMIN_PRIVATE_KEY\|FIREBASE_SERVICE_ACCOUNT" --include="*.env*" --include="*.ts" .

If any of these appear in files under src/, app/, components/, or similar client directories, the credential is in your bundle.

3

Check your deployed JavaScript bundle.

Open your live app in a browser, press F12, go to the Sources tab, and search (Ctrl+F) for private_key_id or iam.gserviceaccount.com. Finding either string in a .js file means the service account is bundled client-side.

CheckYourVibe scans your deployed app for Firebase service account keys in the JavaScript bundle. It also checks whether your Firestore rules allow unauthenticated access, which is a separate but equally serious issue.

Step 2: Rotate the Service Account Key

If the service account JSON was exposed anywhere, rotate it before doing anything else.

1

Open the Google Cloud Console. Go to console.cloud.google.com, select your project, and navigate to IAM & Admin > Service Accounts.

2

Find the Firebase Admin SDK service account. It has an email ending in firebase-adminsdk-XXXXX@your-project-id.iam.gserviceaccount.com. Click it.

3

Add a new key first. Click the Keys tab, then Add Key > Create new key, choose JSON, and download the file. Store it somewhere secure (not your repo).

4

Update your server environment with the new key. Paste the new credentials into your server's environment variables (see Step 5 for the correct format). Redeploy.

5

Delete the old key. Once your server is running with the new key, go back to the Keys tab and delete the compromised key. It is immediately invalidated.

6

Review Cloud Audit Logs. In Google Cloud Console, go to Logging > Logs Explorer and filter by the old client_email to see if unauthorized operations were performed during the exposure window.

Step 3: Remove from Git History (if committed)

If your audit found the file in git history, the key is compromised even after deletion because anyone who cloned the repo before you scrubbed it may still have a copy. Rotate first, then clean.

# Install git-filter-repo (preferred tool)
pip install git-filter-repo

# Remove the service account file from all history
git filter-repo --path firebase-adminsdk.json --invert-paths
git filter-repo --path serviceAccountKey.json --invert-paths
git filter-repo --path firebase-credentials.json --invert-paths

# If you're unsure of the filename, use regex
git filter-repo --paths-glob "**/*service*account*.json" --invert-paths

After scrubbing, force-push to all remotes and ask any collaborators to re-clone. Then add the file pattern to .gitignore:

echo "*service*account*.json" >> .gitignore
echo "firebase-adminsdk*.json" >> .gitignore
echo "*serviceAccountKey*.json" >> .gitignore
git add .gitignore
git commit -m "chore: gitignore firebase service account files"

Step 4: Fix Open Security Rules

Even if no Admin SDK key leaked, open Security Rules leave your entire database readable by anyone who knows your project ID (which is public). In 2022, Cybernews researchers scanned Firebase projects and found more than 900 apps running Firestore or Realtime Database with allow read, write: if true, requiring no authentication at all.

Check your current Firestore rules in the Firebase Console under Firestore Database > Rules.

Insecure (open):

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;  // anyone can do anything
    }
  }
}

Minimum secure baseline:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null;  // must be logged in
    }
  }
}

For user-specific data, scope it further:

match /users/{userId}/{document=**} {
  allow read, write: if request.auth != null && request.auth.uid == userId;
}

Apply the same logic to Realtime Database under Realtime Database > Rules and to Storage under Storage > Rules.

The Firebase Console sends you an email if your rules allow unauthenticated access, but many founders dismiss it as noise. That email is a real alert. The default test-mode rules that Firebase creates when you set up a new project have a 30-day expiry; after that they lock down automatically. If you extended them or copied them without the expiry, check now.

Step 5: Move the Admin SDK Server-Side

The Admin SDK should only ever run in Node.js server code. Here's the correct pattern using environment variables instead of a JSON file:

Wrong (Admin SDK in client-side code):

// src/lib/firebase.ts (client-side code, do not use Admin SDK here)
import admin from 'firebase-admin';
import serviceAccount from './firebase-adminsdk.json'; // never do this

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});

Right (Admin SDK in server-side API route):

// server/api/admin-action.post.ts (Nuxt) or app/api/admin-action/route.ts (Next.js)
import admin from 'firebase-admin';

if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert({
      projectId: process.env.FIREBASE_PROJECT_ID,
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
      // Replace escaped newlines in the private key
      privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
    }),
  });
}

Set these three environment variables on your server:

  • FIREBASE_PROJECT_ID: the project_id value from the JSON
  • FIREBASE_CLIENT_EMAIL: the client_email value
  • FIREBASE_PRIVATE_KEY: the private_key value (the full RSA key string)

The private key contains literal \n characters when stored as an environment variable. That's why the .replace(/\\n/g, '\n') call is necessary.

When copying the private_key value into an environment variable, copy the entire string including the -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- headers. Most platforms (Vercel, Railway, Render) handle multiline values fine just paste the raw string.

Step 6: Prevent Future Leaks

Gitleaks pre-commit hook:

brew install gitleaks  # or: go install github.com/zricethezav/gitleaks/v8@latest

# Test your repo now
gitleaks detect --source . --verbose

# Add as pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
gitleaks protect --staged --verbose
if [ $? -ne 0 ]; then
  echo "Gitleaks found potential secrets. Commit blocked."
  exit 1
fi
EOF
chmod +x .git/hooks/pre-commit

Gitleaks has built-in detection patterns for Firebase service account JSON files. It will block any commit that includes a file containing "type": "service_account".

Firebase App Check adds a second layer by restricting which apps (identified by platform attestation) can use your project's APIs. Enable it in Firebase Console under App Check. It does not replace Security Rules, but it reduces automated scanning of your endpoints.

Is a Firebase API key secret?

The apiKey in your Firebase web SDK config (AIzaSy...) is not a secret. Google intentionally makes it public. It just identifies your project so the SDK can connect. What's secret is your Firebase Admin SDK service account JSON key, which bypasses all Security Rules. Never commit the JSON file or use it in client-side code.

How do I know if my Firebase service account key is exposed?

Search git history with git log --all -p | grep '"type": "service_account"'. Check your deployed JavaScript bundle in browser DevTools for private_key_id or iam.gserviceaccount.com strings. If your Admin SDK import appears in client-side code under src/ or app/, the credentials are reachable from the browser.

What happens if Firebase Security Rules are set to allow read, write: if true?

Any person on the internet with your project ID (which is public) can read and overwrite your entire Firestore or Realtime Database without logging in. In 2022, Cybernews researchers found more than 900 Firebase apps in this state. Replace open rules with at minimum: allow read, write: if request.auth != null.

Can I use the Firebase Admin SDK in a Vite or React app?

No. The Admin SDK is a Node.js library that requires your service account credentials and bypasses all Security Rules. Running it in a browser would expose those credentials to every visitor. Use the Admin SDK only in server-side code: a Cloud Function, an Express server, or a Nuxt/Next.js API route.

How do I rotate a Firebase service account key without downtime?

In Google Cloud Console, go to IAM & Admin > Service Accounts, click the Firebase Admin SDK account, open the Keys tab, and add a new JSON key first. Update your server environment variable with the new credentials and redeploy. Once the new key is confirmed working, delete the old key. This rotation keeps production running throughout.

Check your Firebase deployment for service account keys in your JavaScript bundle, open Security Rules, and missing App Check configuration. Free scan, no signup required.

How-To Guides

How to Fix Firebase API Key Exposure (2026)