Firebase's default configuration is optimized for development speed, not production security. Here's how to close every gap before you go live.
Anay Pandya
2,074 words
Anay Pandya
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
Firebase security failures follow a predictable pattern: a developer ships fast using permissive defaults, forgets to lock down Firestore rules before launch, and discovers the breach weeks later when user data surfaces on a leak forum. The good news is that every layer of the stack — rules, authentication, API keys, hosting headers — has a clear best practice. This guide covers all of them.
<div class="zf-stat-callout" style="background:#0d1117;border:1px solid rgba(16,185,129,0.25);border-left:3px solid #10b981;border-radius:4px;padding:16px 20px;margin:24px 0"> <p style="margin:0 0 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.15em;color:#10b981;font-family:monospace">ZeriFlow Data — 12,400+ sites analyzed</p> <p style="margin:0;font-size:13px;color:#e2e8f0;line-height:1.6;font-family:monospace">ZeriFlow data: the average site passes only 4 out of 11 check your security headers checks. The most commonly missing are Content-Security-Policy (64% absent), Permissions-Policy (77% absent), and HSTS (59% absent).</p> </div>
Keep reading
Is your site actually secure?
Run a free check — 60 seconds
Check your site's security right now: Free ZeriFlow scan →
The single most important Firebase security decision you'll make is your Firestore rules configuration. The default rules for a new project allow full read and write access to all authenticated users — that's every user who has created an account, not just admins.
Start with deny-all and build up:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Deny everything by default
match /{document=**} {
allow read, write: if false;
}
// Users can only read/write their own document
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Orders readable by owner, writable only by backend (service account)
match /orders/{orderId} {
allow read: if request.auth != null && request.auth.uid == resource.data.userId;
allow write: if false; // Server-side only via Admin SDK
}
}
}Critical rule patterns:
request.auth != null before granting any access.request.auth.uid == resource.data.ownerId to ensure users only touch their own data.request.resource.data.keys().hasOnly([...]) to prevent field injection.@firebase/rules-unit-testing) to test your rules before deploying.Never use allow read, write: if true in production. Run firebase deploy --only firestore:rules to push changes, and verify the published rules in the Firebase Console → Firestore → Rules.
Firebase Auth is solid, but several configuration choices significantly affect your security posture.
Email/password authentication:
currentUser.emailVerified. A user who never verified their email may have used a fake address.OAuth providers:
idToken on your backend for any server-side operations. Don't trust client-provided UIDs.[Session management](https://zeriflow.com/blog/session-fixation-attack-prevention):
auth.currentUser.getIdTokenResult() to check claims for role-based access rather than storing roles in Firestore (where users could attempt to modify them).Firebase API keys are visible in client-side code and in your built JavaScript bundles. This is by design — they identify your project. But unrestricted API keys allow anyone to make requests to your Firebase project from anywhere.
Restrict your keys in the Google Cloud Console:
https://yourdomain.com/*.Run a free ZeriFlow scan → to confirm your Firebase Hosting endpoint is properly configured and returns expected security headers.
Service account keys:
Firebase App Hosting (and the legacy Firebase Hosting) allows you to configure HTTP headers via firebase.json. This is the correct place to add security headers — they apply at the CDN edge, before your app even loads.
{
"hosting": {
"headers": [
{
"source": "**",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains; preload" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" },
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self' https://www.gstatic.com https://*.firebaseapp.com; connect-src 'self' https://*.googleapis.com wss://*.firebaseio.com; frame-src 'none'; object-src 'none'"
}
]
}
]
}
}CSP considerations for Firebase apps:
https://www.gstatic.com — include it in script-src.wss://*.firebaseio.com — include in connect-src.Content-Security-Policy-Report-Only and a report-uri endpoint to catch violations before enforcing.Hardcoded credentials in client-side code — or committed to Git — are among the most common Firebase security failures.
For frontend apps (React, Vue, Next.js):
.env files (.env.local, .env.production) for Firebase config values..env*.local to .gitignore — this should already be there in most framework scaffolds, but verify.NEXT_PUBLIC_* and VITE_* variables are bundled into client-side code — visible to anyone who opens DevTools. Firebase config values (apiKey, projectId, etc.) are safe to expose; service account keys are not.For backend/Cloud Functions:
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');
firebase functions:config:set for non-sensitive config, and Secret Manager for sensitive values.CI/CD hygiene:
firebase login:ci) periodically.Firebase Realtime Database has a separate rules system from Firestore. If your project uses both, both need secure rules.
The default Realtime Database rule after disabling test mode is:
{
"rules": {
".read": false,
".write": false
}
}This is the correct starting point. Build from here using auth.uid validation, similar to Firestore. Use .validate rules to enforce data types and shapes at write time.
### Q: Are Firebase API keys safe to expose in client-side JavaScript? A: The Firebase client config (apiKey, projectId, authDomain, etc.) is designed to be public — it identifies your project, it doesn't grant admin access. The actual access control is enforced by Firestore/RTDB security rules and Firebase Auth. That said, you should still add HTTP referrer restrictions in Cloud Console to prevent your quota from being abused by third parties.
### Q: How do I test my Firestore security rules?
A: Use the Firebase Emulator Suite with @firebase/rules-unit-testing. Write unit tests that attempt reads and writes as different users (authenticated, unauthenticated, wrong UID) and assert that access is correctly granted or denied. Run these in CI before every deployment.
### Q: What's the difference between Firebase Hosting and Firebase App Hosting?
A: Firebase Hosting is the original static/SPA hosting product. Firebase App Hosting (2024+) is a newer managed platform for full-stack Next.js, Angular, and similar frameworks with server-side rendering. Both support custom headers via firebase.json or apphosting.yaml.
### Q: How do I know if my Firebase app's public endpoint is missing security headers? A: Run a ZeriFlow scan on your Firebase Hosting URL. It checks for all standard security headers (CSP, HSTS, X-Frame-Options, etc.) and shows exactly which ones are absent or misconfigured.
### Q: Should I use JWT (custom tokens) or Firebase Auth's built-in tokens? A: For most applications, Firebase Auth's built-in ID tokens are the right choice. Use custom tokens only when you need to integrate with an existing external auth system. Whichever you use, always verify tokens server-side via the Firebase Admin SDK before trusting claims.
Firebase's developer experience is excellent, but that same speed-optimized UX ships projects with permissive defaults that need to be actively tightened for production. Firestore rules, API key restrictions, authentication hardening, security headers in firebase.json, and disciplined environment variable management together form a comprehensive Firebase security posture.
Run an external scan to validate what an attacker sees when they hit your production URL.
Run a free ZeriFlow scan → — 60 seconds, no credit card.
<!-- zf-internal-links -->
Firebase App Check verifies that requests to your backend come from your legitimate app, not from unauthorized clients or scrapers.
Enable App Check in your Firebase console, then initialize it in your app:
import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
const app = initializeApp(firebaseConfig);
const appCheck = initializeAppCheck(app, {
provider: new ReCaptchaV3Provider('YOUR_RECAPTCHA_SITE_KEY'),
isTokenAutoRefreshEnabled: true
});Enforce App Check in your Firestore security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.app != null;
}
}
}App Check reduces abuse of your Firebase endpoints by requiring a valid attestation token with every request. This is particularly important for publicly accessible apps where unauthenticated endpoints could otherwise be scraped or abused at scale.
Security rules that were appropriate at launch can become insufficient as your data model grows. A collection added six months ago may have been left with overly permissive rules because no one revisited the Firestore console after the initial deployment.
Audit your rules on a fixed schedule — quarterly at minimum, or after any significant schema change. The Firebase console includes a Rules Playground that lets you simulate read and write operations against your rule set without touching production data. Use it to verify that:
request.auth.uid matches the document owner fieldFor teams managing multiple Firebase projects across staging and production environments, store your security rules in version control and deploy them through CI/CD rather than editing them manually in the console. This creates an auditable history of every rule change.
If you are using Firebase Hosting, security headers are configured in firebase.json under the headers key. Many Firebase projects ship with no headers configured at all, relying entirely on Firebase's defaults — which do not include a Content-Security-Policy, X-Frame-Options, or Permissions-Policy.
A minimal secure headers configuration for Firebase Hosting:
{
"hosting": {
"headers": [
{
"source": "**",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains; preload" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}
}Firebase security configuration can drift over time — especially as team members adjust rules, add new collections, or modify hosting settings.
Run a ZeriFlow scan on your Firebase-hosted application after every major deployment to verify that:
- Security headers are correctly configured in firebase.json
- TLS configuration has not regressed
- No new information disclosure vectors have appeared
Set up ZeriFlow CI/CD integration to scan automatically on every merge to production, giving you a continuous security baseline rather than a point-in-time snapshot.
Check if your Firebase app's headers are configured correctly.
Free security scan — no signup required.
Related resources
Website Vulnerability Scanner
Run a broader website security audit across headers, TLS, DNS, cookies, SEO, and disclosure checks.
Security Headers Checker
Check CSP, HSTS, X-Frame-Options, and other response headers.
SSL Checker
Review TLS certificate, HTTPS, and transport security signals.
DMARC Checker
Validate email authentication records for domain spoofing protection.
CSP Checker
Review Content-Security-Policy coverage and common gaps.
June 24, 2026
Best Security Tools for AI Startups
Monitor your security score
Schedule recurring scans and catch regressions after deployments.