Firebase Security Rules Checklist (Firestore + Storage)

Open Firestore rules let strangers read or write your data. Here are firebase security rules that scope every document and file to its owner, copy-paste ready.

By Artem Vysotskyi · Updated

Test-mode rules, the ones Firebase suggests when you create a new project, read allow read, write: if true. That line lets anyone with your project's URL read or write every document in your database. The fix is auth-scoped rules per collection, locked Storage rules, and a validation step baked into the rule itself. This checklist walks each one with copy-paste rules and an emulator test to confirm before you ship.

How Firebase rules leak data (test mode)

New Firestore and Storage instances start in "test mode": permissive rules meant for local development, with an expiry date after which reads and writes stop entirely. Two things go wrong from here. Either the expiry passes and the app breaks in production (annoying but safe), or someone extends the expiry, or copies the permissive rule into "production mode" rules, to keep shipping without touching the rule file again (works today, leaks tomorrow).

Firebase rules are the only access control Firestore and Storage have. There is no separate database-level permission system behind them the way Postgres has roles and grants. If the rule says yes, the request succeeds, full stop.

Spotting "allow read, write: if true"

This is the line to grep for across every rules file in your project:

match /{document=**} {
  allow read, write: if true;
}

A wildcard match plus an unconditional if true opens everything beneath it. It is easy to miss because it often sits above narrower, safer-looking rules further down the file, and broad rules combine with narrow ones by union: if any matching rule allows the request, the request is allowed. A single leftover wildcard defeats every careful rule you wrote elsewhere.

Auth-scoped Firestore rules (examples)

The replacement pattern checks request.auth before granting anything:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /todos/{todoId} {
      allow read, write: if request.auth != null;
    }
  }
}

request.auth != null is the minimum bar: it blocks anonymous requests but still lets any signed-in user touch any document. That is enough for single-tenant admin tools, not enough for user data.

Per-document owner rules

For user-owned data, scope the rule to the document's owner field:

match /todos/{todoId} {
  allow read, update, delete: if request.auth != null
    && request.auth.uid == resource.data.ownerId;
  allow create: if request.auth != null
    && request.auth.uid == request.resource.data.ownerId;
}

Note the split: resource.data is the document as it already exists in the database (used for read, update, delete), while request.resource.data is the document as the client is trying to write it (used for create, and for validating updates). Mixing these up is a common source of rules that look correct and are not.

Lock Storage rules

Storage rules are a separate file from Firestore rules and are just as often left in test mode. The same pattern applies:

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

Anything outside a matched, authenticated path in this file is unreachable by URL, which is what you want for user uploads, private documents, and anything not meant to be a public asset.

Validate data shape in rules

Auth checks answer "who," not "what." A signed-in user can still write a document with the wrong shape, an oversized field, or a value from a range you never intended, unless the rule validates it:

allow update: if request.auth.uid == resource.data.ownerId
  && request.resource.data.title is string
  && request.resource.data.title.size() < 200;

This is the Firestore-rules equivalent of a server-side input check. Skipping it means your client-side validation is the only validation, and a client is something anyone can bypass.

Test rules with the emulator

Firebase ships a local emulator suite specifically so you can test rules without touching production data. Run it, write a small test file with two fake users, and assert that user A cannot read user B's documents and that an unauthenticated request is rejected outright. This catches the mistake where a rule works in the console's rules playground but fails once real client SDKs and real auth tokens are involved.

Verify from outside

Once rules are deployed, confirm from outside your app, the same way an attacker would: an unauthenticated curl against your Firestore REST endpoint, and against a Storage file URL you expect to be private, should both fail. Config review tells you what you intended to allow. Only an outside request tells you what the rule actually allows.

See what a stranger can read from your app, free

FAQ

What does "allow read, write: if true" do?

It grants the request unconditionally, no authentication or ownership check at all. Combined with a wildcard path match, it makes every document under that path readable and writable by anyone who has your project's API endpoint, which is visible in your client bundle by design.

Is the Firebase apiKey in my config secret?

No. The `apiKey` in your Firebase client config is a project identifier, not a secret, Google's own documentation says as much. It is meant to ship in your client code. Your actual security boundary is your Firestore and Storage rules, not this key.

How do I test Firebase rules?

Run the Firebase Local Emulator Suite and write rule tests with two or more fake authenticated users. Assert that each user can read and write only their own data, and that unauthenticated requests are rejected. This is more reliable than eyeballing the rules file, because it exercises the rules the way your real client SDK does.

Do security rules apply to the Admin SDK?

No. The Admin SDK, used from trusted server environments, bypasses Firestore and Storage rules entirely by design, it operates with full access. Rules only govern client-side reads and writes through the Firebase client SDKs. Never expose Admin SDK credentials to the browser, that would hand out full access with none of the rule protections.

Find out in 30 seconds

Paste your URL and LeakRank shows what a stranger can actually read from your app. Free, no signup.

Scan my app free →

Related guides