[{"data":1,"prerenderedAt":356},["ShallowReactive",2],{"blog-how-to/firebase-security-rules":3},{"id":4,"title":5,"body":6,"category":338,"date":339,"dateModified":339,"description":340,"draft":341,"extension":342,"faq":343,"featured":341,"headerVariant":344,"image":343,"keywords":343,"meta":345,"navigation":346,"ogDescription":347,"ogTitle":343,"path":293,"readTime":343,"schemaOrg":348,"schemaType":349,"seo":350,"sitemap":351,"stem":352,"tags":353,"twitterCard":354,"__hash__":355},"blog/blog/how-to/firebase-security-rules.md","How to Write Firebase Security Rules",{"type":7,"value":8,"toc":318},"minimark",[9,13,17,21,27,36,41,46,57,61,67,71,77,81,87,91,97,101,104,107,113,117,123,127,131,150,154,160,164,175,242,279,299],[10,11],"category-badge",{"category":12},"How-To Guide",[14,15,5],"h1",{"id":16},"how-to-write-firebase-security-rules",[18,19,20],"p",{},"Protect your Firestore and Realtime Database",[22,23,24],"tldr",{},[18,25,26],{},"TL;DR:\nUse\nrequest.auth.uid\nto verify the user owns the data. Never use\nallow read, write: if true\nin production. Test rules in the Firebase console before deploying. Firestore rules are different from Realtime Database rules.",[28,29,30,33],"warning-box",{},[18,31,32],{},"Default Rules Are Dangerous",[18,34,35],{},"Firebase projects often start with \"test mode\" rules that allow anyone to read and write all data. These rules expire after 30 days, but many developers forget to update them. Always check your rules before launching.",[37,38,40],"h2",{"id":39},"firestore-security-rules","Firestore Security Rules",[42,43,45],"h3",{"id":44},"basic-structure","Basic Structure",[47,48,53],"pre",{"className":49,"code":51,"language":52},[50],"language-text","rules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n    // Rules go here\n  }\n}\n","text",[54,55,51],"code",{"__ignoreMap":56},"",[42,58,60],{"id":59},"pattern-1-user-owned-documents","Pattern 1: User-Owned Documents",[47,62,65],{"className":63,"code":64,"language":52},[50],"rules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n\n    // Users can only access their own documents\n    match /users/{userId} {\n      allow read, write: if request.auth != null\n                         && request.auth.uid == userId;\n    }\n\n    // Todos owned by users\n    match /todos/{todoId} {\n      allow read: if request.auth != null\n                  && resource.data.userId == request.auth.uid;\n\n      allow create: if request.auth != null\n                    && request.resource.data.userId == request.auth.uid;\n\n      allow update, delete: if request.auth != null\n                            && resource.data.userId == request.auth.uid;\n    }\n  }\n}\n",[54,66,64],{"__ignoreMap":56},[42,68,70],{"id":69},"pattern-2-public-read-authenticated-write","Pattern 2: Public Read, Authenticated Write",[47,72,75],{"className":73,"code":74,"language":52},[50],"match /posts/{postId} {\n  // Anyone can read published posts\n  allow read: if resource.data.status == 'published';\n\n  // Only authenticated users can create posts\n  allow create: if request.auth != null\n                && request.resource.data.authorId == request.auth.uid;\n\n  // Only the author can update/delete\n  allow update, delete: if request.auth != null\n                        && resource.data.authorId == request.auth.uid;\n}\n",[54,76,74],{"__ignoreMap":56},[42,78,80],{"id":79},"pattern-3-data-validation","Pattern 3: Data Validation",[47,82,85],{"className":83,"code":84,"language":52},[50],"match /products/{productId} {\n  allow create: if request.auth != null\n                // Required fields\n                && request.resource.data.keys().hasAll(['name', 'price', 'createdAt'])\n                // Type validation\n                && request.resource.data.name is string\n                && request.resource.data.price is number\n                && request.resource.data.price > 0\n                // Timestamp validation\n                && request.resource.data.createdAt == request.time;\n}\n",[54,86,84],{"__ignoreMap":56},[42,88,90],{"id":89},"pattern-4-role-based-access","Pattern 4: Role-Based Access",[47,92,95],{"className":93,"code":94,"language":52},[50],"// Assumes users have a 'role' field in their profile\nmatch /admin/{document=**} {\n  allow read, write: if request.auth != null\n                     && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin';\n}\n\n// Or use custom claims (set via Admin SDK)\nmatch /admin/{document=**} {\n  allow read, write: if request.auth != null\n                     && request.auth.token.admin == true;\n}\n",[54,96,94],{"__ignoreMap":56},[37,98,100],{"id":99},"realtime-database-rules","Realtime Database Rules",[18,102,103],{},"Realtime Database uses a different JSON-based syntax:",[42,105,45],{"id":106},"basic-structure-1",[47,108,111],{"className":109,"code":110,"language":52},[50],"{\n  \"rules\": {\n    \".read\": false,\n    \".write\": false,\n    // Specific rules override these defaults\n  }\n}\n",[54,112,110],{"__ignoreMap":56},[42,114,116],{"id":115},"user-owned-data","User-Owned Data",[47,118,121],{"className":119,"code":120,"language":52},[50],"{\n  \"rules\": {\n    \"users\": {\n      \"$userId\": {\n        \".read\": \"$userId === auth.uid\",\n        \".write\": \"$userId === auth.uid\"\n      }\n    },\n    \"todos\": {\n      \"$todoId\": {\n        \".read\": \"data.child('userId').val() === auth.uid\",\n        \".write\": \"(!data.exists() && newData.child('userId').val() === auth.uid) ||\n                   (data.child('userId').val() === auth.uid)\"\n      }\n    }\n  }\n}\n",[54,122,120],{"__ignoreMap":56},[37,124,126],{"id":125},"testing-your-rules","Testing Your Rules",[42,128,130],{"id":129},"firebase-console","Firebase Console",[132,133,134,138,141,144,147],"ol",{},[135,136,137],"li",{},"Go to Firebase Console → Firestore → Rules",[135,139,140],{},"Click \"Rules Playground\"",[135,142,143],{},"Select operation type (get, list, create, update, delete)",[135,145,146],{},"Enter document path and optional auth context",[135,148,149],{},"Run the simulation",[42,151,153],{"id":152},"emulator-testing","Emulator Testing",[47,155,158],{"className":156,"code":157,"language":52},[50],"// Install Firebase emulators\nfirebase init emulators\n\n// Start emulators\nfirebase emulators:start\n\n// In your test file\nimport { assertFails, assertSucceeds } from '@firebase/rules-unit-testing';\n\ndescribe('Firestore rules', () => {\n  it('allows users to read their own data', async () => {\n    const db = getTestFirestore({ uid: 'user123' });\n    await assertSucceeds(db.collection('users').doc('user123').get());\n  });\n\n  it('denies users from reading others data', async () => {\n    const db = getTestFirestore({ uid: 'user123' });\n    await assertFails(db.collection('users').doc('other-user').get());\n  });\n});\n",[54,159,157],{"__ignoreMap":56},[37,161,163],{"id":162},"common-mistakes","Common Mistakes",[28,165,166,169],{},[18,167,168],{},"Never Do This in Production",[47,170,173],{"className":171,"code":172,"language":52},[50],"// DANGEROUS: Anyone can read/write everything\nmatch /{document=**} {\n  allow read, write: if true;\n}\n\n// DANGEROUS: Allows all authenticated users full access\nmatch /{document=**} {\n  allow read, write: if request.auth != null;\n}\n",[54,174,172],{"__ignoreMap":56},[176,177,178,194],"table",{},[179,180,181],"thead",{},[182,183,184,188,191],"tr",{},[185,186,187],"th",{},"Mistake",[185,189,190],{},"Problem",[185,192,193],{},"Fix",[195,196,197,209,220,231],"tbody",{},[182,198,199,203,206],{},[200,201,202],"td",{},"if true",[200,204,205],{},"Anyone can access",[200,207,208],{},"Always check auth and ownership",[182,210,211,214,217],{},[200,212,213],{},"if request.auth != null",[200,215,216],{},"Any logged-in user can access",[200,218,219],{},"Verify user owns the data",[182,221,222,225,228],{},[200,223,224],{},"No validation on create",[200,226,227],{},"Users can set any userId",[200,229,230],{},"Validate request.resource.data.userId",[182,232,233,236,239],{},[200,234,235],{},"Recursive wildcards",[200,237,238],{},"{document=**} is too broad",[200,240,241],{},"Be specific about paths",[243,244,245,267,273],"faq-section",{},[246,247,249],"faq-item",{"question":248},"What's the difference between resource and request.resource?",[18,250,251,254,255,258,259,262,263,266],{},[54,252,253],{},"resource.data"," contains the existing document data. ",[54,256,257],{},"request.resource.data"," contains the data being written. Use ",[54,260,261],{},"resource"," for read/delete operations and ",[54,264,265],{},"request.resource"," for create/update validation.",[246,268,270],{"question":269},"Why are my rules not working?",[18,271,272],{},"Common issues: 1) Rules haven't been deployed yet, 2) You're using the Admin SDK which bypasses rules, 3) Path matching is incorrect, 4) Auth context isn't being passed correctly.",[246,274,276],{"question":275},"How do I debug rules?",[18,277,278],{},"Use the Rules Playground in Firebase Console, check the Firebase logs in Google Cloud Console, or use the Firebase emulator with debug logging enabled.",[18,280,281,285,290,291,290,295],{},[282,283,284],"strong",{},"Related guides:",[286,287,289],"a",{"href":288},"/blog/guides/firebase","Firebase Security Guide"," ·\n",[286,292,294],{"href":293},"/blog/how-to/firebase-security-rules","Firebase Auth Rules",[286,296,298],{"href":297},"/blog/comparisons/supabase-vs-firebase","Supabase vs Firebase Security",[300,301,302,308,313],"related-articles",{},[303,304],"related-card",{"description":305,"href":306,"title":307},"Step-by-step guide to protecting routes and API endpoints. Implement middleware patterns, authentication guards, authori","/blog/how-to/protect-routes","How to Protect Routes and API Endpoints",[303,309],{"description":310,"href":311,"title":312},"Complete guide to configuring environment variables in Railway. Set up secrets, use variable references, and manage conf","/blog/how-to/railway-env-vars","How to Set Up Railway Environment Variables",[303,314],{"description":315,"href":316,"title":317},"Step-by-step guide to rate limiting authentication endpoints. Prevent brute force attacks, credential stuffing, and acco","/blog/how-to/rate-limiting-auth","How to Implement Rate Limiting for Authentication",{"title":56,"searchDepth":319,"depth":319,"links":320},2,[321,329,333,337],{"id":39,"depth":319,"text":40,"children":322},[323,325,326,327,328],{"id":44,"depth":324,"text":45},3,{"id":59,"depth":324,"text":60},{"id":69,"depth":324,"text":70},{"id":79,"depth":324,"text":80},{"id":89,"depth":324,"text":90},{"id":99,"depth":319,"text":100,"children":330},[331,332],{"id":106,"depth":324,"text":45},{"id":115,"depth":324,"text":116},{"id":125,"depth":319,"text":126,"children":334},[335,336],{"id":129,"depth":324,"text":130},{"id":152,"depth":324,"text":153},{"id":162,"depth":319,"text":163},"how-to","2026-01-14","Complete guide to Firebase Firestore and Realtime Database security rules. Learn rule syntax, common patterns, testing, and debugging your Firebase security.",false,"md",null,"yellow",{},true,"Complete guide to Firebase security rules for Firestore and Realtime Database.","[object Object]","HowTo",{"title":5,"description":340},{"loc":293},"blog/how-to/firebase-security-rules",[],"summary_large_image","6nL42ZDefu-CcKfN34RI_znO1v2LHFq7qJ2uJHclCtI",1775843928342]