Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Set Firestore Rules to Restrict Access

Firestore security rules control who can read and write your data using a declarative DSL. Rules match document paths with wildcards and evaluate conditions using request.auth (the authenticated user), request.resource.data (incoming write data), and resource.data (existing document data). The most important patterns are request.auth != null for authenticated-only access, request.auth.uid == userId for user-scoped data, and data validation on writes. Rules are deployed with firebase deploy --only firestore:rules and take up to 10 minutes to propagate.

What you'll learn

  • How to write Firestore security rules for authentication and authorization
  • How to validate incoming data on writes
  • How to use custom claims for role-based access control
  • How to test and deploy security rules
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate9 min read20-25 minFirestore (all plans), Firebase CLI for deploymentMarch 2026RapidDev Engineering Team
TL;DR

Firestore security rules control who can read and write your data using a declarative DSL. Rules match document paths with wildcards and evaluate conditions using request.auth (the authenticated user), request.resource.data (incoming write data), and resource.data (existing document data). The most important patterns are request.auth != null for authenticated-only access, request.auth.uid == userId for user-scoped data, and data validation on writes. Rules are deployed with firebase deploy --only firestore:rules and take up to 10 minutes to propagate.

Setting Firestore Security Rules to Restrict Access

Firestore security rules are the server-side enforcement layer that protects your data. Without proper rules, anyone with your Firebase config can read or modify your entire database. This tutorial covers the rule syntax, common patterns for authentication, user ownership, role-based access, data validation, and how to test and deploy rules. Every production Firestore database needs well-crafted security rules.

Prerequisites

  • A Firebase project with Firestore enabled
  • Firebase CLI installed and logged in (npm install -g firebase-tools)
  • Firebase Authentication configured with at least one sign-in method
  • Understanding of your Firestore data structure (collections and document fields)

Step-by-step guide

1

Understand the rules structure and matching

Firestore rules are defined in a firestore.rules file. The top-level structure wraps all rules in a service declaration. Inside, match statements bind to document paths using wildcards. The {database} wildcard matches the database name, and {document=**} matches any document path recursively. Rules evaluate top-down — the most specific match wins. read permission splits into get (single document) and list (queries), and write splits into create, update, and delete.

typescript
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4
5 // This matches ALL documents (use sparingly)
6 match /{document=**} {
7 allow read, write: if false; // Default deny
8 }
9
10 // This matches documents in the 'posts' collection
11 match /posts/{postId} {
12 allow read: if true; // Public read
13 allow write: if false; // No writes
14 }
15
16 // This matches subcollection documents
17 match /posts/{postId}/comments/{commentId} {
18 allow read: if true;
19 allow create: if request.auth != null;
20 }
21 }
22}

Expected result: All documents are denied by default. The posts collection is publicly readable but not writable. Comments are publicly readable and creatable by authenticated users.

2

Require authentication for all access

The most fundamental rule checks request.auth != null to ensure the user is signed in. Apply this to collections that should only be accessible to logged-in users. You can combine auth checks with more specific conditions. The request.auth object contains uid, token (with email, custom claims), and provider information.

typescript
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4
5 // Authenticated users only
6 match /profiles/{userId} {
7 allow read: if request.auth != null;
8 allow write: if request.auth != null
9 && request.auth.uid == userId;
10 }
11
12 // Require email verification
13 match /premium/{docId} {
14 allow read: if request.auth != null
15 && request.auth.token.email_verified == true;
16 }
17 }
18}

Expected result: Only authenticated users can read profiles. Only the profile owner can write their profile. Premium content requires email verification.

3

Implement user ownership rules

For user-generated content, verify that the authenticated user owns the document. On create, check that the incoming data includes the correct author ID. On update and delete, check the existing document's author ID against the current user. This prevents users from modifying or deleting other users' content.

typescript
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4 match /posts/{postId} {
5 // Anyone signed in can read
6 allow read: if request.auth != null;
7
8 // Only create if the authorId matches the current user
9 allow create: if request.auth != null
10 && request.resource.data.authorId == request.auth.uid;
11
12 // Only update/delete your own posts
13 allow update: if request.auth != null
14 && resource.data.authorId == request.auth.uid
15 && request.resource.data.authorId == request.auth.uid;
16
17 allow delete: if request.auth != null
18 && resource.data.authorId == request.auth.uid;
19 }
20 }
21}

Expected result: Users can only create posts with their own UID as authorId, can only update or delete their own posts, and cannot change the authorId field.

4

Validate incoming data on writes

Use request.resource.data to validate the structure and content of incoming writes. Check field types with 'is string', 'is number', etc. Validate string lengths, number ranges, and required fields. This prevents malicious or malformed data from being written to your database. Combine validation with authentication and ownership checks.

typescript
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4 match /posts/{postId} {
5 allow create: if request.auth != null
6 && request.resource.data.authorId == request.auth.uid
7 && request.resource.data.title is string
8 && request.resource.data.title.size() > 0
9 && request.resource.data.title.size() <= 200
10 && request.resource.data.body is string
11 && request.resource.data.body.size() <= 10000
12 && request.resource.data.createdAt == request.time
13 && request.resource.data.keys().hasAll(['authorId', 'title', 'body', 'createdAt']);
14
15 allow update: if request.auth != null
16 && resource.data.authorId == request.auth.uid
17 && request.resource.data.diff(resource.data).affectedKeys()
18 .hasOnly(['title', 'body', 'updatedAt']);
19 }
20 }
21}

Expected result: Documents can only be created with valid titles (1-200 chars), bodies (up to 10000 chars), and server timestamps. Updates can only modify title, body, and updatedAt.

5

Implement role-based access with custom claims

For admin or moderator roles, use Firebase custom claims set via the Admin SDK. Access custom claims in rules through request.auth.token. Set custom claims server-side — they cannot be set from the client. Custom claims are stored in the user's ID token and are limited to 1000 bytes total.

typescript
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4
5 // Admin-only collection
6 match /adminSettings/{docId} {
7 allow read, write: if request.auth != null
8 && request.auth.token.role == 'admin';
9 }
10
11 // Posts: anyone reads, authors write, admins can delete any
12 match /posts/{postId} {
13 allow read: if request.auth != null;
14 allow create: if request.auth != null
15 && request.resource.data.authorId == request.auth.uid;
16 allow update: if request.auth != null
17 && resource.data.authorId == request.auth.uid;
18 allow delete: if request.auth != null
19 && (resource.data.authorId == request.auth.uid
20 || request.auth.token.role == 'admin');
21 }
22 }
23}

Expected result: Admin settings are only accessible to users with the admin role claim. Admins can delete any post while regular users can only delete their own.

6

Test rules in the Rules Playground and deploy

Before deploying, test your rules using the Rules Playground in Firebase Console. Navigate to Firestore > Rules > Edit rules and use the Playground tab to simulate read and write operations with different auth states. Once verified, deploy with the Firebase CLI. Rules take up to 1 minute for new queries and up to 10 minutes for active listeners to pick up changes.

typescript
1# Deploy only Firestore rules
2firebase deploy --only firestore:rules
3
4# Test rules with the emulator
5firebase emulators:start --only firestore
6
7# Run automated rule tests (with @firebase/rules-unit-testing)
8npm test

Expected result: Rules are deployed and take effect within minutes. The Rules Playground confirms that access is correctly restricted.

Complete working example

firestore.rules
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4
5 // Helper function: is the user signed in?
6 function isSignedIn() {
7 return request.auth != null;
8 }
9
10 // Helper function: does the user own this document?
11 function isOwner(userId) {
12 return isSignedIn() && request.auth.uid == userId;
13 }
14
15 // Helper function: is the user an admin?
16 function isAdmin() {
17 return isSignedIn() && request.auth.token.role == 'admin';
18 }
19
20 // User profiles: owner reads/writes, others can read
21 match /profiles/{userId} {
22 allow read: if isSignedIn();
23 allow create, update: if isOwner(userId)
24 && request.resource.data.displayName is string
25 && request.resource.data.displayName.size() > 0
26 && request.resource.data.displayName.size() <= 50;
27 allow delete: if isOwner(userId) || isAdmin();
28 }
29
30 // Posts: public read, author writes, admin moderates
31 match /posts/{postId} {
32 allow read: if isSignedIn();
33 allow create: if isSignedIn()
34 && request.resource.data.authorId == request.auth.uid
35 && request.resource.data.title is string
36 && request.resource.data.title.size() > 0
37 && request.resource.data.title.size() <= 200
38 && request.resource.data.body is string
39 && request.resource.data.body.size() <= 10000;
40 allow update: if isSignedIn()
41 && resource.data.authorId == request.auth.uid;
42 allow delete: if isSignedIn()
43 && (resource.data.authorId == request.auth.uid || isAdmin());
44
45 // Post comments
46 match /comments/{commentId} {
47 allow read: if isSignedIn();
48 allow create: if isSignedIn()
49 && request.resource.data.authorId == request.auth.uid
50 && request.resource.data.text is string
51 && request.resource.data.text.size() > 0
52 && request.resource.data.text.size() <= 2000;
53 allow delete: if isSignedIn()
54 && (resource.data.authorId == request.auth.uid || isAdmin());
55 }
56 }
57
58 // Admin settings: admin only
59 match /settings/{docId} {
60 allow read, write: if isAdmin();
61 }
62
63 // Default deny
64 match /{document=**} {
65 allow read, write: if false;
66 }
67 }
68}

Common mistakes when setting Firestore Rules to Restrict Access

Why it's a problem: Using 'allow read, write: if true' in production, leaving the entire database open to anyone

How to avoid: Never use unconditional allow rules in production. Start with 'if false' as the default and add specific conditions for each collection.

Why it's a problem: Thinking security rules act as filters — they reject entire queries that could return unauthorized documents

How to avoid: Firestore evaluates whether a query could potentially return unauthorized documents based on its constraints. Your query must include filters that match your rules. For example, if rules require authorId == request.auth.uid, your query must include where('authorId', '==', currentUser.uid).

Why it's a problem: Forgetting to deploy rules after editing them in firestore.rules, leaving old rules in production

How to avoid: Run firebase deploy --only firestore:rules after every rules change. Consider adding this to your CI/CD pipeline so rules are always deployed with code changes.

Why it's a problem: Not validating incoming data fields on create, allowing malformed documents in the database

How to avoid: Add type checks (is string, is number), size constraints, and required field checks to all create and update rules.

Best practices

  • Start with a default deny rule (allow read, write: if false) and explicitly allow access for each collection
  • Use helper functions (isSignedIn, isOwner, isAdmin) to keep rules DRY and readable
  • Always validate incoming data types and sizes on create and update operations
  • Use request.resource.data.diff(resource.data).affectedKeys().hasOnly() to restrict which fields can be updated
  • Set custom claims for role-based access rather than checking a Firestore roles document (which adds a read per request)
  • Test rules with the Rules Playground and automate testing with @firebase/rules-unit-testing in CI
  • Deploy rules as part of your CI/CD pipeline to ensure they stay in sync with your application code
  • Remember that rules are not filters — your queries must include constraints that match your rules

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Firestore database with users, posts, and comments collections. Write production-ready security rules that require authentication for all access, let users only modify their own data, validate post titles (1-200 chars) and bodies (max 10000 chars), support admin role via custom claims, and include helper functions for common checks.

Firebase Prompt

Generate a complete Firestore security rules file for a blog application with: user profiles (owner read/write), posts (public read, author write, admin delete), nested comments (auth create, author/admin delete), admin settings (admin only), and a default deny rule. Include helper functions for isSignedIn, isOwner, isAdmin, and data validation.

Frequently asked questions

Do Firestore security rules filter query results?

No. Security rules are not filters. Firestore evaluates whether a query could potentially return unauthorized documents. If it could, the entire query is rejected, even if all actual results would be authorized. Your query constraints must match what your rules allow.

How long do rules take to propagate after deployment?

New rules take effect within about 1 minute for new queries. Active real-time listeners may take up to 10 minutes to use the new rules. During deployment, there is no downtime — old rules remain active until new ones propagate.

Can I use Firestore data in security rules (like checking a roles document)?

Yes. Use get() and exists() functions to read other documents in rules. For example: get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.isAdmin. However, each get() counts as a document read and adds latency. Custom claims are faster and free.

What is the maximum complexity of security rules?

Security rules support custom functions with up to 10 let bindings and a call stack depth of 10. Each rule evaluation can make up to 10 get() and exists() calls. Rules are limited to 256 KB in size.

Should I use custom claims or Firestore documents for roles?

Use custom claims for roles checked in security rules. Claims are stored in the auth token (no extra read), limited to 1000 bytes, and set server-side. Use Firestore documents for complex role structures with many permissions, but be aware each rules get() call adds latency and a document read.

Can RapidDev help design and implement Firestore security rules for my application?

Yes. RapidDev can design security rules tailored to your data model, including role-based access, data validation, and automated testing to ensure your database is properly secured.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.