Appearance
Firestore Security Rules Cheat Sheet
Basic Structure
js
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Match rules go here
}
}Match All Documents
js
match /{document=**} {
allow read, write: if false; // Deny all
}Basic Read/Write Access
Allow All Reads
js
match /{document=**} {
allow read: if true;
}Allow All Writes
js
match /{document=**} {
allow write: if true;
}Deny All Access
js
match /{document=**} {
allow read, write: if false;
}User Authentication
Allow Only Authenticated Users
js
match /posts/{postId} {
allow read, write: if request.auth != null;
}User-Based Document Access
Users Can Only Read/Write Their Own Documents
js
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}Role-Based Access
Assume a customClaims field is added via Firebase Admin SDK:
js
function isAdmin() {
return request.auth.token.admin == true;
}Admin-Only Access
js
match /adminData/{docId} {
allow read, write: if isAdmin();
}Field-Level Validation
Only Allow Writes If Specific Fields Meet Conditions
js
match /posts/{postId} {
allow update: if request.resource.data.keys().hasOnly(['title', 'content']) &&
request.resource.data.title is string &&
request.resource.data.content is string;
}Document Create vs Update
js
match /posts/{postId} {
allow create: if request.auth != null;
allow update: if request.auth.uid == resource.data.authorId;
}Advanced Conditions
Check Document Ownership Before Write
js
match /posts/{postId} {
allow write: if request.auth.uid == resource.data.authorId;
}Validate Data on Write
js
match /posts/{postId} {
allow write: if request.resource.data.title.size() < 100;
}Collection Group Rules
For subcollections across documents:
js
match /databases/{database}/documents {
match /users/{userId}/posts/{postId} {
allow read: if request.auth.uid == userId;
}
}Match Nested Collections
js
match /users/{userId} {
allow read: if request.auth.uid == userId;
match /posts/{postId} {
allow read, write: if request.auth.uid == userId;
}
}Pagination / Query-Based Rules
You cannot access query parameters in rules. But you can restrict sorting/filtering by only allowing indexes that match the rules.
Always validate all documents in the result:
js
match /posts/{postId} {
allow read: if resource.data.isPublished == true;
}Time-Based Access
js
match /events/{eventId} {
allow read: if resource.data.startTime > request.time;
}Prevent Changing a Field After Creation
js
match /users/{userId} {
allow update: if request.resource.data.email == resource.data.email;
}Allow Users to Create But Not Read
js
match /feedback/{entryId} {
allow create: if request.auth != null;
allow read: if false;
}Allow Public Read, Authenticated Write
js
match /posts/{postId} {
allow read: if true;
allow write: if request.auth != null;
}Enforce Schema / Type Checking
js
match /posts/{postId} {
allow write: if
request.resource.data.title is string &&
request.resource.data.likes is int &&
request.resource.data.createdAt is timestamp;
}Custom Functions
You can create reusable helper functions:
js
function isSignedIn() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId;
}Usage:
js
match /users/{userId} {
allow read: if isSignedIn() && isOwner(userId);
}Prevent Deletes
js
match /posts/{postId} {
allow delete: if false;
}Allow Access to Own Posts Only
js
match /posts/{postId} {
allow read, write: if request.auth.uid == resource.data.userId;
}Allow Writes for a Limited Time Period
js
match /submissions/{id} {
allow write: if request.time < timestamp.date(2025, 3, 30);
}Read-Only Admin Panel
js
match /admin/{docId} {
allow read: if isAdmin();
allow write: if false;
}Tips & Gotchas
- Rules are NOT filters — they do not limit query results after retrieval. Every document must individually match the rule.
- Always test rules in the Firebase console or via the emulator.
- Always validate the schema manually in the rules if needed.
- Avoid overly complex rules — split access into documents or subcollections when needed.