Structuring Security Rules

Cloud Firestore Security Rules allow you to control access to documents and collections in your database. The flexible rules syntax allows you to create rules that match anything, from all writes to the entire database to operations on a specific document.

This guide describes the basic syntax and structure of security rules. Combine this syntax with security rules conditions to create complete rulesets.

Service and database declaration

Cloud Firestore Security Rules always begin with the following declaration:

service cloud.firestore {
  match /databases/{database}/documents {
    // ...
  }
}

The service cloud.firestore declaration scopes the rules to Cloud Firestore, preventing conflicts between Cloud Firestore Security Rules and rules for other products such as Cloud Storage.

The match /databases/{database}/documents declaration specifies that rules should match any Cloud Firestore database in the project. Currently each project has only a single database named (default).

Basic read/write rules

Basic rules consist of a match statement specifying a document path and an allow expression detailing when reading the specified data is allowed:

service cloud.firestore {
  match /databases/{database}/documents {

    // Match any document in the 'cities' collection
    match /cities/{city} {
      allow read: if <condition>;
      allow write: if <condition>;
    }
  }
}

All match statements should point to documents, not collections. A match statement can point to a specific document, as in match /cities/SF or use wildcards to point to any document in the specified path, as in match /cities/{city}.

In the example above, the match statement uses the {city} wildcard syntax. This means the rule applies to any document in the cities collection, such as /cities/SF or /cities/NYC. When the allow expressions in the match statement are evaluated, the city variable will resolve to the city document name, such as SF or NYC.

Granular operations

In some situations, it's useful to break down read and write into more granular operations. For example, your app may want to enforce different conditions on document creation than on document deletion. Or you may want to allow single document reads but deny large queries.

A read rule can be broken into get and list, while a write rule can be broken into create, update, and delete:

service cloud.firestore {
  match /databases/{database}/documents {
    // A read rule can be divided into get and list rules
    match /cities/{city} {
      // Applies to single document read requests
      allow get: if <condition>;

      // Applies to queries and collection read requests
      allow list: if <condition>;
    }

    // A write rule can be divided into create, update, and delete rules
    match /cities/{city} {
      // Applies to writes to nonexistent documents
      allow create: if <condition>;

      // Applies to writes to existing documents
      allow update: if <condition>;

      // Applies to delete operations
      allow delete: if <condition>;
    }
  }
}

Hierarchical data

Data in Cloud Firestore is organized into collections of documents, and each document may extend the hierarchy through subcollections. It is important to understand how security rules interact with hierarchical data.

Consider the situation where each document in the cities collection contains a landmarks subcollection. Security rules apply only at the matched path, so the access controls defined on the cities collection do not apply to the landmarks subcollection. Instead, write explicit rules to control access to subcollections:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if <condition>;

        // Explicitly define rules for the 'landmarks' subcollection
        match /landmarks/{landmark} {
          allow read, write: if <condition>;
        }
    }
  }
}

When nesting match statements, the path of the inner match statement is always relative to the path of the outer match statement. The following rulesets are therefore equivalent:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city}/landmarks/{landmark} {
      allow read, write: if <condition>;
    }
  }
}

If you want rules to apply to an arbitrarily deep hierarchy, use the recursive wildcard syntax, {name=**}:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

When using the recursive wildcard syntax, the wildcard variable will contain the entire matching path segment, even if the document is located in a deeply nested subcollection. For example, the rules listed above would match a document located at /cities/SF/landmarks/coit_tower, and the value of the document variable would be SF/landmarks/coit_tower.

Recursive wildcards cannot match an empty path, so match /cities/{city}/{document=**} will match documents in subcollections but not in the cities collection, whereas match /cities/{document=**} will match both documents in the cities collection and subcollections.

It's possible for a document to match more than one match statement. In the case where multiple allow expressions match a request, the access is allowed if any of the conditions is true:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the 'cities' collection.
    match /cities/{city} {
      allow read, write: if false;
    }

    // Matches any document in the 'cities' collection or subcollections.
    match /cities/{document=**} {
      allow read, write: if true;
    }
  }
}

In the example above, all reads and writes to the cities collection will be allowed because the second rule is always true, even though the first rule is always false.

Security rule limits

As you work with security rules, note the following limits:

Limit Details
Maximum number of exists(), get(), and getAfter() calls per request
  • 10 for single-document requests and query requests.
  • 20 for multi-document reads, transactions, and batched writes. The previous limit of 10 also applies to each operation.

    For example, imagine you create a batched write request with 3 write operations and that your security rules use 2 document access calls to validate each write. In this case, each write uses 2 of its 10 access calls and the batched write request uses 6 of its 20 access calls.

Exceeding either limit results in a permission denied error.

Some document access calls may be cached, and cached calls do not count towards the limits.

Maximum function call depth 20
Maximum number of recursive or cyclical function calls 0 (not permitted)
Maximum number of expressions in a ruleset 10,000
Maximum size of a ruleset 64 KB

Next steps