new: drive vas from your AI agent over MCP · Cursor, Claude Code, Windsurf
Firebase Security

Firebase Security Rules: Copy-Paste Examples

Firestore, Realtime Database, and Cloud Storage rules you can paste in and adapt, each with a plain-English note on when to use it.

Most AI-generated Firebase apps ship with test-mode rules still on. This page shows what to replace them with.

What Firebase security rules actually control

Firebase doesn't put a traditional backend API between your app and your data. The client SDK talks to Firestore, Realtime Database, or Cloud Storage directly. Security rules are the only thing standing between a request and your data, there is no hidden server-side check happening anywhere else. If a rule allows it, it happens. If it doesn't, Firebase rejects the request before it touches storage.

That responsibility is split across three separate rule surfaces, each with its own file and its own syntax:

Firestore

Document-based rules in firestore.rules, written in the Firebase rules language.

Realtime Database

JSON rules in database.rules.json, evaluated per path in the tree.

Cloud Storage

File-path rules in storage.rules, similar syntax to Firestore.

Securing one doesn't secure the others. An app that locks down Firestore but leaves the default Storage rules in place is still exposing every uploaded file.

The test-mode trap

When you create a new Firestore database, the console offers a “Start in test mode” option so you can build without fighting rules first. It writes this:

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

if true means the rule always passes. Anyone who has your project's config, which is public by design, can read and write every document in the database. No login, no API key check, nothing.

Test mode expires 30 days after creation and then defaults to denying everything, which breaks the app rather than securing it properly. The common failure mode isn't “forgot to write rules”, it's shipping to production inside that 30-day window with test rules still active, or extending the expiry date instead of writing real rules. Bots actively scan for exposed Firebase projects, so this isn't a hypothetical risk window.

Firestore security rules examples

Start from “locked by default” and add exactly the access your app needs. Each example below is a working match block you can adapt to your own collection names.

Locked by default

Use this when: Use this as your starting point before adding any access.

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

Authenticated users only

Use this when: Use this when any signed-in user should read and write, but anonymous visitors should not.

match /databases/{database}/documents {
  match /{document=**} {
    allow read, write: if request.auth != null;
  }
}

User owns the document

Use this when: Use this for per-user data like profiles, orders, or settings, where each doc belongs to exactly one uid.

match /databases/{database}/documents {
  match /orders/{orderId} {
    allow read, write: if request.auth != null
      && request.auth.uid == resource.data.userId;
    allow create: if request.auth != null
      && request.auth.uid == request.resource.data.userId;
  }
}

Role-based via custom claims

Use this when: Use this when some users need broader access, like admins or support staff, set through Firebase Auth custom claims.

match /databases/{database}/documents {
  match /reports/{reportId} {
    allow read: if request.auth != null
      && (request.auth.uid == resource.data.ownerId
          || request.auth.token.role == 'admin');
    allow write: if request.auth != null
      && request.auth.token.role == 'admin';
  }
}

Validated writes

Use this when: Use this to stop clients from writing malformed or oversized data, even from an authenticated account.

match /databases/{database}/documents {
  match /profiles/{userId} {
    allow write: if request.auth.uid == userId
      && request.resource.data.keys().hasAll(['displayName', 'bio'])
      && request.resource.data.displayName is string
      && request.resource.data.displayName.size() < 60
      && request.resource.data.bio.size() < 500;
  }
}

Public read, authenticated write

Use this when: Use this for content like blog posts or listings, where anyone should read but only the author should write.

match /databases/{database}/documents {
  match /posts/{postId} {
    allow read: if true;
    allow create: if request.auth != null
      && request.resource.data.authorId == request.auth.uid;
    allow update, delete: if request.auth != null
      && resource.data.authorId == request.auth.uid;
  }
}

Realtime Database rules examples

Realtime Database rules are plain JSON, evaluated top-down by path. A $uid segment matches any key at that level, letting you write one rule that applies per user.

Default deny

Use this when: Use this at the root so nothing is exposed until you explicitly open a path.

{
  "rules": {
    ".read": false,
    ".write": false
  }
}

Per-user node

Use this when: Use this for a /users/$uid structure where each user should only touch their own branch.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

Validated fields

Use this when: Use this to enforce shape and type on writes, the Realtime Database equivalent of a schema check.

{
  "rules": {
    "messages": {
      "$uid": {
        "$messageId": {
          ".write": "$uid === auth.uid",
          ".validate": "newData.hasChildren(['text', 'createdAt']) && newData.child('text').isString() && newData.child('text').val().length < 1000"
        }
      }
    }
  }
}

Cloud Storage rules examples

Storage rules match on file paths the same way Firestore rules match on documents. Scope every path to the uploading user, and constrain what they're allowed to upload.

User-scoped upload paths

Use this when: Use this so each user can only read and write inside their own folder, like /uploads/{uid}/.

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /uploads/{userId}/{fileName} {
      allow read, write: if request.auth != null
        && request.auth.uid == userId;
    }
  }
}

Content-type and size limits

Use this when: Use this to stop uploads from being used to store arbitrary files or exhaust your storage budget.

match /b/{bucket}/o {
  match /avatars/{userId}/{fileName} {
    allow write: if request.auth != null
      && request.auth.uid == userId
      && request.resource.size < 5 * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
  }
}

Rules that look secure but are not

allow read, write: if auth != null on shared data

Impact: Any logged-in user, not just the owner, can read or overwrite every other user's documents.
Fix: Add an ownership check, like request.auth.uid == resource.data.userId, on top of the auth check.

Validating on create but not on update

Impact: A crafted update request can rewrite a document to drop required fields or change values the create rule blocked.
Fix: Apply the same request.resource.data validation to allow update as you do to allow create.

Using get() or exists() without expecting the cost

Impact: Rules that call get() to check a related document work correctly but add a billed document read to every request, and can hit a 20-call-per-request limit in deeply nested checks.
Fix: Cache role or ownership data on the document itself (like a denormalized ownerId) instead of doing a cross-document lookup on every read.

Enforcing limits only in client-side code

Impact: A field length check or role check that only exists in your app's JavaScript does nothing against a direct write via the Firebase SDK or REST API with valid credentials.
Fix: Move every constraint that matters into the rules themselves. Client-side checks are UX, rules are security.

Testing rules before you deploy them

Firebase Emulator Suite

Run rules locally against a real emulator instead of production data:

firebase emulators:start

Pair it with @firebase/rules-unit-testing to write unit tests that assert specific reads and writes are allowed or denied, so a rule regression fails CI instead of shipping.

Rules Playground

For a quick manual check, the Rules tab in the Firebase console has a Playground that simulates a request as a chosen user, authenticated or not, against a chosen document or path. It shows exactly which line of your rules allowed or denied the request.

Useful for a fast sanity check, but the emulator and unit tests are what catch a regression before it reaches production.

Check whether your Firebase rules actually hold

vas tests your live Firebase app for exposed data, permissive rules, and test-mode configurations left on past launch. Run your first scan free to see your security score and every issue count in minutes. Unlock every finding with a copy-paste fix from $19/month.

Run your first scan free

Frequently Asked Questions

Are Firebase security rules enough?

For most apps, yes, as long as the rules actually deny by default and validate every field a client can write. Rules are your only backend-side access control on Firestore, Realtime Database, and Storage, since there's no separate API layer checking permissions. The gap is usually not "rules vs. no rules", it's rules that look strict but leave a hole, like checking request.auth != null without also checking ownership.

What happens if I have no rules, or test-mode rules?

allow read, write: if true grants anyone with your project ID full read and write access to every document, node, or file. No login required. Bots scan for exposed Firebase projects specifically, so an app left in test mode past its 30-day expiry window is not theoretical risk, it gets found.

Are Firebase API keys secret?

No. The apiKey in your Firebase client config is designed to be public, it identifies your project, the same way a URL does. It is not a credential and doesn't need to be hidden or rotated if it shows up in your bundled JS. Your security boundary is the rules, not the key.

How do I test Firebase rules before deploying them?

Run the Firebase Local Emulator Suite (firebase emulators:start) and write unit tests against @firebase/rules-unit-testing that assert specific reads and writes are allowed or denied. For a quick manual check, use the Rules Playground in the Firebase console, which lets you simulate a request as a given user without touching production data.

Do Firestore, Realtime Database, and Storage share one rules file?

No, each is a separate rules language and a separate deploy target: firestore.rules, database.rules.json (or storage.rules for Cloud Storage), all listed in firebase.json. Securing Firestore does nothing for a Storage bucket left wide open, they need to be written and deployed independently.

Last updated: August 29, 2026