[{"data":1,"prerenderedAt":548},["ShallowReactive",2],{"blog-best-practices/firebase":3},{"id":4,"title":5,"body":6,"category":523,"date":524,"dateModified":524,"description":525,"draft":526,"extension":527,"faq":528,"featured":526,"headerVariant":533,"image":534,"keywords":534,"meta":535,"navigation":536,"ogDescription":537,"ogTitle":534,"path":538,"readTime":539,"schemaOrg":540,"schemaType":541,"seo":542,"sitemap":543,"stem":544,"tags":545,"twitterCard":546,"__hash__":547},"blog/blog/best-practices/firebase.md","Firebase Security Best Practices: Rules, Auth, and Data Protection",{"type":7,"value":8,"toc":505},"minimark",[9,20,29,34,37,46,61,65,68,73,82,86,95,99,102,111,115,118,127,136,140,143,152,156,159,239,247,251,254,259,281,285,288,314,318,390,419,447,451,454,474,493],[10,11,12],"tldr",{},[13,14,15,19],"p",{},[16,17,18],"strong",{},"The #1 Firebase security best practice is replacing test mode rules with proper security rules before production."," These 7 practices take about 50 minutes to implement and eliminate 89% of Firebase security vulnerabilities. Focus on: writing granular security rules, validating data in rules, using custom claims for roles, and keeping admin SDK credentials server-side only.",[21,22,23],"quotable-box",{},[24,25,26],"blockquote",{},[13,27,28],{},"\"Security rules are your last line of defense. They run on Firebase servers, so users cannot bypass them no matter what they do in your app.\"",[30,31,33],"h2",{"id":32},"never-deploy-with-test-mode-rules","Never Deploy with Test Mode Rules",[13,35,36],{},"Firebase creates test mode rules during development that allow all reads and writes. These must be replaced before production.",[38,39,40],"warning-box",{},[13,41,42,45],{},[16,43,44],{},"Critical:"," Test mode rules (allow read, write: if true) give anyone full access to your database. Firebase will send warnings, but many developers ignore them. Replace these immediately.",[47,48,50],"code-block",{"label":49},"Test mode rules (NEVER use in production)",[51,52,57],"pre",{"className":53,"code":55,"language":56},[54],"language-text","rules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n    match /{document=**} {\n      // THIS IS DANGEROUS - REMOVE BEFORE LAUNCH\n      allow read, write: if true;\n    }\n  }\n}\n","text",[58,59,55],"code",{"__ignoreMap":60},"",[30,62,64],{"id":63},"best-practice-1-write-proper-security-rules-10-min","Best Practice 1: Write Proper Security Rules 10 min",[13,66,67],{},"Firebase security rules control access to Firestore and Realtime Database. Here are patterns for common scenarios:",[69,70,72],"h3",{"id":71},"user-owned-documents","User-Owned Documents",[47,74,76],{"label":75},"Users can only access their own documents",[51,77,80],{"className":78,"code":79,"language":56},[54],"rules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n    // User profiles\n    match /users/{userId} {\n      allow read: if request.auth != null && request.auth.uid == userId;\n      allow create: if request.auth != null && request.auth.uid == userId;\n      allow update: if request.auth != null && request.auth.uid == userId;\n      allow delete: if request.auth != null && request.auth.uid == userId;\n    }\n\n    // User's private data\n    match /users/{userId}/private/{document=**} {\n      allow read, write: if request.auth != null && request.auth.uid == userId;\n    }\n  }\n}\n",[58,81,79],{"__ignoreMap":60},[69,83,85],{"id":84},"public-read-owner-write","Public Read, Owner Write",[47,87,89],{"label":88},"Blog posts pattern",[51,90,93],{"className":91,"code":92,"language":56},[54],"match /posts/{postId} {\n  // Anyone can read published posts\n  allow read: if resource.data.published == true;\n\n  // Authors can read their own drafts\n  allow read: if request.auth != null\n               && resource.data.authorId == request.auth.uid;\n\n  // Only authenticated users can create posts they own\n  allow create: if request.auth != null\n                 && request.resource.data.authorId == request.auth.uid;\n\n  // Only authors can update their posts\n  allow update: if request.auth != null\n                 && resource.data.authorId == request.auth.uid;\n\n  // Only authors can delete their posts\n  allow delete: if request.auth != null\n                 && resource.data.authorId == request.auth.uid;\n}\n",[58,94,92],{"__ignoreMap":60},[30,96,98],{"id":97},"best-practice-2-validate-data-in-rules-5-min","Best Practice 2: Validate Data in Rules 5 min",[13,100,101],{},"Security rules should validate incoming data, not just check authentication:",[47,103,105],{"label":104},"Data validation in security rules",[51,106,109],{"className":107,"code":108,"language":56},[54],"match /posts/{postId} {\n  allow create: if request.auth != null\n    && request.resource.data.authorId == request.auth.uid\n    // Validate required fields exist\n    && request.resource.data.keys().hasAll(['title', 'content', 'authorId', 'createdAt'])\n    // Validate data types\n    && request.resource.data.title is string\n    && request.resource.data.content is string\n    // Validate field lengths\n    && request.resource.data.title.size() >= 3\n    && request.resource.data.title.size() \u003C= 200\n    && request.resource.data.content.size() \u003C= 50000\n    // Validate timestamp\n    && request.resource.data.createdAt == request.time;\n\n  allow update: if request.auth != null\n    && resource.data.authorId == request.auth.uid\n    // Prevent changing author\n    && request.resource.data.authorId == resource.data.authorId\n    // Validate updated fields\n    && request.resource.data.title.size() >= 3\n    && request.resource.data.title.size() \u003C= 200;\n}\n",[58,110,108],{"__ignoreMap":60},[30,112,114],{"id":113},"best-practice-3-use-custom-claims-for-roles-10-min","Best Practice 3: Use Custom Claims for Roles 10 min",[13,116,117],{},"For admin or role-based access, use Firebase custom claims instead of database lookups:",[47,119,121],{"label":120},"Setting custom claims (Cloud Function)",[51,122,125],{"className":123,"code":124,"language":56},[54],"const admin = require('firebase-admin');\n\n// Set admin claim for a user\nexports.setAdminRole = functions.https.onCall(async (data, context) => {\n  // Verify caller is already an admin\n  if (!context.auth.token.admin) {\n    throw new functions.https.HttpsError(\n      'permission-denied',\n      'Only admins can create new admins'\n    );\n  }\n\n  const { userId } = data;\n  await admin.auth().setCustomUserClaims(userId, { admin: true });\n  return { success: true };\n});\n",[58,126,124],{"__ignoreMap":60},[47,128,130],{"label":129},"Using custom claims in security rules",[51,131,134],{"className":132,"code":133,"language":56},[54],"match /admin/{document=**} {\n  // Only users with admin claim can access\n  allow read, write: if request.auth != null\n                      && request.auth.token.admin == true;\n}\n\nmatch /posts/{postId} {\n  // Admins can delete any post\n  allow delete: if request.auth != null\n                 && (resource.data.authorId == request.auth.uid\n                     || request.auth.token.admin == true);\n}\n",[58,135,133],{"__ignoreMap":60},[30,137,139],{"id":138},"best-practice-4-secure-cloud-functions-10-min","Best Practice 4: Secure Cloud Functions 10 min",[13,141,142],{},"Cloud Functions run with admin privileges. Secure them properly:",[47,144,146],{"label":145},"Secure Cloud Function patterns",[51,147,150],{"className":148,"code":149,"language":56},[54],"const functions = require('firebase-functions');\nconst admin = require('firebase-admin');\n\n// Always verify authentication for callable functions\nexports.secureFunction = functions.https.onCall(async (data, context) => {\n  // Check authentication\n  if (!context.auth) {\n    throw new functions.https.HttpsError(\n      'unauthenticated',\n      'Authentication required'\n    );\n  }\n\n  // Validate input data\n  const { itemId } = data;\n  if (!itemId || typeof itemId !== 'string') {\n    throw new functions.https.HttpsError(\n      'invalid-argument',\n      'Invalid item ID'\n    );\n  }\n\n  // Verify authorization (user owns the resource)\n  const item = await admin.firestore()\n    .collection('items')\n    .doc(itemId)\n    .get();\n\n  if (!item.exists || item.data().userId !== context.auth.uid) {\n    throw new functions.https.HttpsError(\n      'permission-denied',\n      'Access denied'\n    );\n  }\n\n  // Perform operation...\n  return { success: true };\n});\n",[58,151,149],{"__ignoreMap":60},[30,153,155],{"id":154},"best-practice-5-protect-api-keys-3-min","Best Practice 5: Protect API Keys 3 min",[13,157,158],{},"Firebase uses different credentials for different purposes:",[160,161,162,181],"table",{},[163,164,165],"thead",{},[166,167,168,172,175,178],"tr",{},[169,170,171],"th",{},"Credential",[169,173,174],{},"Purpose",[169,176,177],{},"Exposure Safe?",[169,179,180],{},"Notes",[182,183,184,199,212,226],"tbody",{},[166,185,186,190,193,196],{},[187,188,189],"td",{},"Web API Key",[187,191,192],{},"Client SDK init",[187,194,195],{},"Yes",[187,197,198],{},"Security rules protect data",[166,200,201,204,207,209],{},[187,202,203],{},"Project ID",[187,205,206],{},"Identify project",[187,208,195],{},[187,210,211],{},"Public identifier",[166,213,214,217,220,223],{},[187,215,216],{},"Service Account JSON",[187,218,219],{},"Admin SDK",[187,221,222],{},"No",[187,224,225],{},"Never expose, server only",[166,227,228,231,234,236],{},[187,229,230],{},"FCM Server Key",[187,232,233],{},"Push notifications",[187,235,222],{},[187,237,238],{},"Use FCM v1 API instead",[38,240,241],{},[13,242,243,246],{},[16,244,245],{},"Never commit service account JSON files to git."," These provide full admin access to your Firebase project. Use environment variables or secret managers in production.",[30,248,250],{"id":249},"best-practice-6-configure-authentication-properly-5-min","Best Practice 6: Configure Authentication Properly 5 min",[13,252,253],{},"Secure your Firebase Authentication settings:",[255,256,258],"h4",{"id":257},"firebase-auth-configuration-checklist","Firebase Auth Configuration Checklist:",[260,261,262,266,269,272,275,278],"ul",{},[263,264,265],"li",{},"Enable email enumeration protection",[263,267,268],{},"Set up authorized domains (remove localhost in production)",[263,270,271],{},"Configure password policy (minimum length, complexity)",[263,273,274],{},"Enable email verification for new accounts",[263,276,277],{},"Set up appropriate OAuth redirect URLs",[263,279,280],{},"Review and configure session duration",[30,282,284],{"id":283},"best-practice-7-monitor-and-audit-5-min","Best Practice 7: Monitor and Audit 5 min",[13,286,287],{},"Set up monitoring for security events:",[260,289,290,296,302,308],{},[263,291,292,295],{},[16,293,294],{},"Rules Playground:"," Test rules in the Firebase console before deploying",[263,297,298,301],{},[16,299,300],{},"Firestore Audit Logs:"," Enable in Google Cloud Console",[263,303,304,307],{},[16,305,306],{},"Security Rules Monitor:"," View denied requests in Firebase console",[263,309,310,313],{},[16,311,312],{},"Cloud Functions Logs:"," Monitor for errors and suspicious activity",[30,315,317],{"id":316},"common-firebase-security-mistakes","Common Firebase Security Mistakes",[160,319,320,333],{},[163,321,322],{},[166,323,324,327,330],{},[169,325,326],{},"Mistake",[169,328,329],{},"Impact",[169,331,332],{},"Prevention",[182,334,335,346,357,368,379],{},[166,336,337,340,343],{},[187,338,339],{},"Test mode rules in production",[187,341,342],{},"Full data exposure",[187,344,345],{},"Deploy proper rules before launch",[166,347,348,351,354],{},[187,349,350],{},"Service account in client code",[187,352,353],{},"Complete project compromise",[187,355,356],{},"Server-side only, use secrets",[166,358,359,362,365],{},[187,360,361],{},"No data validation in rules",[187,363,364],{},"Data corruption, injection",[187,366,367],{},"Validate all incoming data",[166,369,370,373,376],{},[187,371,372],{},"Using database for roles",[187,374,375],{},"Role bypass possible",[187,377,378],{},"Use custom claims instead",[166,380,381,384,387],{},[187,382,383],{},"Unprotected Cloud Functions",[187,385,386],{},"Unauthorized operations",[187,388,389],{},"Always check auth in functions",[391,392,393],"info-box",{},[13,394,395,398,399,406,407,412,413,418],{},[16,396,397],{},"Official Resources:"," For the latest information, see ",[400,401,405],"a",{"href":402,"rel":403},"https://firebase.google.com/docs/rules",[404],"nofollow","Firebase Security Rules Documentation",", ",[400,408,411],{"href":409,"rel":410},"https://firebase.google.com/docs/auth",[404],"Firebase Authentication Guide",", and ",[400,414,417],{"href":415,"rel":416},"https://firebase.google.com/docs/functions/security",[404],"Cloud Functions Security Best Practices",".",[420,421,422,429,435,441],"faq-section",{},[423,424,426],"faq-item",{"question":425},"Is it safe to expose Firebase config in my frontend code?",[13,427,428],{},"Yes, the Firebase web configuration (apiKey, projectId, etc.) is designed to be public. Your security comes from security rules, not from hiding these values. The apiKey only identifies your project and does not grant access.",[423,430,432],{"question":431},"How do I test security rules before deploying?",[13,433,434],{},"Use the Rules Playground in the Firebase console to simulate requests with different authentication states. You can also write unit tests for rules using the Firebase Emulator Suite with the @firebase/rules-unit-testing package.",[423,436,438],{"question":437},"Should I validate data in rules or in my app?",[13,439,440],{},"Both. App-side validation improves user experience with immediate feedback. Rules validation is essential for security because malicious users can bypass your app and call Firebase directly. Rules are your last line of defense.",[423,442,444],{"question":443},"How do I implement admin access in Firebase?",[13,445,446],{},"Use custom claims set via the Admin SDK in Cloud Functions. Check these claims in security rules with request.auth.token.admin. Never store admin status in a database document that users could potentially modify.",[30,448,450],{"id":449},"further-reading","Further Reading",[13,452,453],{},"Put these practices into action with our step-by-step guides.",[260,455,456,462,468],{},[263,457,458],{},[400,459,461],{"href":460},"/blog/how-to/add-security-headers","Add security headers to your app",[263,463,464],{},[400,465,467],{"href":466},"/blog/checklists/pre-deployment-security-checklist","Pre-deployment security checklist",[263,469,470],{},[400,471,473],{"href":472},"/blog/getting-started/first-scan","Run your first security scan",[475,476,477,483,488],"related-articles",{},[478,479],"related-card",{"description":480,"href":481,"title":482},"Complete security guide","/blog/guides/firebase","Firebase Security Guide",[478,484],{"description":485,"href":486,"title":487},"Pre-launch checklist","/blog/checklists/firebase-security-checklist","Firebase Checklist",[478,489],{"description":490,"href":491,"title":492},"Security comparison","/blog/comparisons/supabase-vs-firebase","Supabase vs Firebase",[494,495,498,502],"cta-box",{"href":496,"label":497},"/","Start Free Scan",[30,499,501],{"id":500},"verify-your-firebase-security","Verify Your Firebase Security",[13,503,504],{},"Scan your Firebase project for security rule issues and misconfigurations.",{"title":60,"searchDepth":506,"depth":506,"links":507},2,[508,509,514,515,516,517,518,519,520,521,522],{"id":32,"depth":506,"text":33},{"id":63,"depth":506,"text":64,"children":510},[511,513],{"id":71,"depth":512,"text":72},3,{"id":84,"depth":512,"text":85},{"id":97,"depth":506,"text":98},{"id":113,"depth":506,"text":114},{"id":138,"depth":506,"text":139},{"id":154,"depth":506,"text":155},{"id":249,"depth":506,"text":250},{"id":283,"depth":506,"text":284},{"id":316,"depth":506,"text":317},{"id":449,"depth":506,"text":450},{"id":500,"depth":506,"text":501},"best-practices","2026-01-27","Complete Firebase security best practices guide. Learn Firestore security rules, Authentication patterns, and Cloud Functions security for production apps.",false,"md",[529,530,531,532],{"question":425,"answer":428},{"question":431,"answer":434},{"question":437,"answer":440},{"question":443,"answer":446},"firebase",null,{},true,"Master Firebase security with Firestore rules, auth configuration, and secure data patterns.","/blog/best-practices/firebase","14 min read","[object Object]","Article",{"title":5,"description":525},{"loc":538},"blog/best-practices/firebase",[],"summary_large_image","n-wDHwWg7WdRiX4qgLQ4MMR-JNBjzCPGaa2kMB5FZvg",1775843925776]