Use conditions in Realtime Database Security Rules

This guide builds on thelearn the core Firebase Security Rules language guideto show how to add conditions to your Firebase Realtime Database Security Rules.

The primary building block of Realtime Database Security Rules is thecondition. Acondition is a Boolean expression that determines whether a particular operationshould be allowed or denied. For basic rules, usingtrue andfalse literals asconditions works prefectly well. But the Realtime Database Security Rules language gives youways to write more complex conditions that can:

  • Check user authentication
  • Evaluate existing data against newly-submitted data
  • Access and compare different parts of your database
  • Validate incoming data
  • Use the structure of incoming queries for security logic

Using $ Variables to Capture Path Segments

You can capture portions of the path for a read or write by declaringcapture variables with the$ prefix.This serves as a wild card, and stores the value of that key for use insiderules conditions:

{"rules":{"rooms":{//thisruleappliestoanychildof/rooms/,thekeyforeachroomid//isstoredinside$room_idvariableforreference"$room_id":{"topic":{//theroom's topic can be changed if the room id has "public" in it".write":"$room_id.contains('public')"}}}}}

The dynamic$ variables can also be used in parallel with constant pathnames. In this example, we're using the$other variable to declarea.validate rule that ensures thatwidget has no children other thantitle andcolor.Any write that would result in additional children being created would fail.

{  "rules": {    "widget": {      // a widget can have a title or color attribute      "title": { ".validate": true },      "color": { ".validate": true },      // but no other child paths are allowed      // in this case, $other means any key excluding "title" and "color"      "$other": { ".validate": false }    }  }}
Note: Path keys are always strings. For this reason, it's important to keep in mindthat when we attempt to compare a$ variable to a number, this willalways fail. This can be corrected by converting the number to a string(e.g.$key === newData.val()+'').

Authentication

One of the most common security rule patterns is controlling access based on theuser's authentication state. For example, your app may want to allow onlysigned-in users to write data.

If your app uses Firebase Authentication, therequest.auth variable containsthe authentication information for the client requesting data.For more information aboutrequest.auth, seethe referencedocumentation.

Firebase Authentication integrates with theFirebase Realtime Database to allow you to control dataaccess on a per-user basis using conditions. Once a user authenticates, theauthvariable in your Realtime Database Security Rules rules will be populated with the user'sinformation. This information includes their unique identifier (uid)as well as linked account data, such as a Facebook id or an email address, andother info. If you implement a custom auth provider, you can add your own fieldsto your user's auth payload.

This section explains how to combine the Firebase Realtime Database Security Rules language withauthentication information about your users. By combining these two concepts,you can control access to data based on user identity.

Theauth Variable

The predefinedauth variable in the rules is null beforeauthentication takes place.

Once a user is authenticated withFirebase Authenticationit will contain the following attributes:

provider The authentication method used ("password", "anonymous", "facebook", "github", "google", or "twitter").
uid A unique user id, guaranteed to be unique across all providers.
token The contents of the Firebase Auth ID token. See the reference documentation forauth.token for more details.

Here is an example rule that uses theauth variable to ensure thateach user can only write to a user-specific path:

{  "rules": {    "users": {      "$user_id": {        // grants write access to the owner of this user account        // whose uid must exactly match the key ($user_id)        ".write": "$user_id === auth.uid"      }    }  }}

Structuring Your Database to Support Authentication Conditions

It is usually helpful to structure your database in a way that makes writingSecurity Rules easier. One common pattern for storing user data in theRealtime Database isto store all of your users in a singleusers node whose children aretheuid values for every user. If you wanted to restrict access tothis data such that only the logged-in user can see their own data, your ruleswould look something like this.

{  "rules": {    "users": {      "$uid": {        ".read": "auth !== null && auth.uid === $uid"      }    }  }}

Working with Authentication Custom Claims

For apps that require custom access control for different users,Firebase Authenticationallows developers toset claims on a Firebase user.These claims are accessible in theauth.token variable in your rules.Here is an example of rules that make use of thehasEmergencyTowelcustom claim:

{  "rules": {    "frood": {      // A towel is about the most massively useful thing an interstellar      // hitchhiker can have      ".read": "auth.token.hasEmergencyTowel === true"    }  }}

Developers creating their owncustom authentication tokens can optionally add claims to these tokens. Theseclaims are available on theauth.token variable in your rules.

Existing Data vs. New Data

The predefineddata variable is used to refer to the data beforea write operation takes place. Conversely, thenewData variablecontains the new data that will exist if the write operation is successful.newData represents the merged result of the new data being writtenand existing data.

To illustrate, this rule would allow us to create new records or delete existingones, but not to make changes to existing non-null data:

// we can write as long as old data or new data does not exist// in other words, if this is a delete or a create, but not an update".write":"!data.exists() || !newData.exists()"
Make sure to check for null or invalid data. Errors inrules lead to rejected operations.

Referencing Data in other Paths

Any data can be used as criterion for rules. Using the predefinedvariablesroot,data, andnewData, wecan access any path as it would exist before or after a write event.

Consider this example, which allows write operations as long as the value of the/allow_writes/ node istrue, the parent node does not have areadOnly flag set, and there is a child namedfoo inthe newly written data:

".write": "root.child('allow_writes').val() === true &&          !data.parent().child('readOnly').exists() &&          newData.child('foo').exists()"

Validating Data

Enforcing data structures and validating the format and content of data shouldbe done using.validate rules, which are run only after a.write rule succeeds to grant access. Below is a sample.validate rule definition which only allows dates in the formatYYYY-MM-DD between the years 1900-2099, which is checked using a regular expression.

".validate": "newData.isString() &&              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"
Try it on JSFiddle:Click here to see this in action. Try writing different values to the input field.

The.validate rules are the only type of security rule which do not cascade. If anyvalidation rule fails on any child record, the entire write operation will be rejected.Additionally, the validate definitions are ignored when data is deleted (that is, when the new valuebeing written isnull).

Note: The.validate rules are only evaluated for non-null values and do not cascade.

These might seem like trivial points, but are in fact significant features for writingpowerful Firebase Realtime Database Security Rules. Consider the following rules:

{  "rules": {    // write is allowed for all paths    ".write": true,    "widget": {      // a valid widget must have attributes "color" and "size"      // allows deleting widgets (since .validate is not applied to delete rules)      ".validate": "newData.hasChildren(['color', 'size'])",      "size": {        // the value of "size" must be a number between 0 and 99        ".validate": "newData.isNumber() &&                      newData.val() >= 0 &&                      newData.val() <= 99"      },      "color": {        // the value of "color" must exist as a key in our mythical        // /valid_colors/ index        ".validate": "root.child('valid_colors/' + newData.val()).exists()"      }    }  }}

With this variant in mind, look at the results for the following write operations:

JavaScript
varref=db.ref("/widget");// PERMISSION_DENIED: does not have children color and sizeref.set('foo');// PERMISSION DENIED: does not have child colorref.set({size:22});// PERMISSION_DENIED: size is not a numberref.set({size:'foo',color:'red'});// SUCCESS (assuming 'blue' appears in our colors list)ref.set({size:21,color:'blue'});// If the record already exists and has a color, this will// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])// will fail to validateref.child('size').set(99);
Objective-C
Note: This Firebase product is not available on the App Clip target.
FIRDatabaseReference*ref=[[[FIRDatabasedatabase]reference]child:@"widget"];// PERMISSION_DENIED: does not have children color and size[refsetValue:@"foo"];// PERMISSION DENIED: does not have child color[refsetValue:@{@"size":@"foo"}];// PERMISSION_DENIED: size is not a number[refsetValue:@{@"size":@"foo",@"color":@"red"}];// SUCCESS (assuming 'blue' appears in our colors list)[refsetValue:@{@"size":@21,@"color":@"blue"}];// If the record already exists and has a color, this will// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])// will fail to validate[[refchild:@"size"]setValue:@99];
Swift
Note: This Firebase product is not available on the App Clip target.
varref=FIRDatabase.database().reference().child("widget")// PERMISSION_DENIED: does not have children color and sizeref.setValue("foo")// PERMISSION DENIED: does not have child colorref.setValue(["size":"foo"])// PERMISSION_DENIED: size is not a numberref.setValue(["size":"foo","color":"red"])// SUCCESS (assuming 'blue' appears in our colors list)ref.setValue(["size":21,"color":"blue"])// If the record already exists and has a color, this will// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])// will fail to validateref.child("size").setValue(99);
Java
FirebaseDatabasedatabase=FirebaseDatabase.getInstance();DatabaseReferenceref=database.getReference("widget");// PERMISSION_DENIED: does not have children color and sizeref.setValue("foo");// PERMISSION DENIED: does not have child colorref.child("size").setValue(22);// PERMISSION_DENIED: size is not a numberMap<String,Object>map=newHashMap<String,Object>();map.put("size","foo");map.put("color","red");ref.setValue(map);// SUCCESS (assuming 'blue' appears in our colors list)map=newHashMap<String,Object>();map.put("size",21);map.put("color","blue");ref.setValue(map);// If the record already exists and has a color, this will// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])// will fail to validateref.child("size").setValue(99);
REST
# PERMISSION_DENIED: does not have children color and sizecurl -X PUT -d 'foo' \https://docs-examples.firebaseio.com/rest/securing-data/example.json# PERMISSION DENIED: does not have child colorcurl -X PUT -d '{"size": 22}' \https://docs-examples.firebaseio.com/rest/securing-data/example.json# PERMISSION_DENIED: size is not a numbercurl -X PUT -d '{"size": "foo", "color": "red"}' \https://docs-examples.firebaseio.com/rest/securing-data/example.json# SUCCESS (assuming 'blue' appears in our colors list)curl -X PUT -d '{"size": 21, "color": "blue"}' \https://docs-examples.firebaseio.com/rest/securing-data/example.json# If the record already exists and has a color, this will# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])# will fail to validatecurl -X PUT -d '99' \https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Now let's look at the same structure, but using.write rules instead of.validate:

{"rules":{//thisvariantwillNOTallowdeletingrecords(since.writewouldbedisallowed)"widget":{//awidgetmusthave'color'and'size'inordertobewrittentothispath".write":"newData.hasChildren(['color', 'size'])","size":{//thevalueof"size"mustbeanumberbetween0and99,ONLYIFWEWRITEDIRECTLYTOSIZE".write":"newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"},"color":{//thevalueof"color"mustexistasakeyinourmythicalvalid_colors/index//BUTONLYIFWEWRITEDIRECTLYTOCOLOR".write":"root.child('valid_colors/'+newData.val()).exists()"}}}}

In this variant, any of the following operations would succeed:

JavaScript
varref=newFirebase(URL+"/widget");// ALLOWED? Even though size is invalid, widget has children color and size,// so write is allowed and the .write rule under color is ignoredref.set({size:99999,color:'red'});// ALLOWED? Works even if widget does not exist, allowing us to create a widget// which is invalid and does not have a valid color.// (allowed by the write rule under "color")ref.child('size').set(99);
Objective-C
Note: This Firebase product is not available on the App Clip target.
Firebase*ref=[[Firebasealloc]initWithUrl:URL];// ALLOWED? Even though size is invalid, widget has children color and size,// so write is allowed and the .write rule under color is ignored[refsetValue:@{@"size":@9999,@"color":@"red"}];// ALLOWED? Works even if widget does not exist, allowing us to create a widget// which is invalid and does not have a valid color.// (allowed by the write rule under "color")[[refchildByAppendingPath:@"size"]setValue:@99];
Swift
Note: This Firebase product is not available on the App Clip target.
varref=Firebase(url:URL)// ALLOWED? Even though size is invalid, widget has children color and size,// so write is allowed and the .write rule under color is ignoredref.setValue(["size":9999,"color":"red"])// ALLOWED? Works even if widget does not exist, allowing us to create a widget// which is invalid and does not have a valid color.// (allowed by the write rule under "color")ref.childByAppendingPath("size").setValue(99)
Java
Firebaseref=newFirebase(URL+"/widget");// ALLOWED? Even though size is invalid, widget has children color and size,// so write is allowed and the .write rule under color is ignoredMap<String,Object>map=newHashMap<String,Object>();map.put("size",99999);map.put("color","red");ref.setValue(map);// ALLOWED? Works even if widget does not exist, allowing us to create a widget// which is invalid and does not have a valid color.// (allowed by the write rule under "color")ref.child("size").setValue(99);
REST
# ALLOWED? Even though size is invalid, widget has children color and size,# so write is allowed and the .write rule under color is ignoredcurl -X PUT -d '{size: 99999, color: "red"}' \https://docs-examples.firebaseio.com/rest/securing-data/example.json# ALLOWED? Works even if widget does not exist, allowing us to create a widget# which is invalid and does not have a valid color.# (allowed by the write rule under "color")curl -X PUT -d '99' \https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

This illustrates the differences between.write and.validate rules.As demonstrated, all of these rules should be written using.validate, with thepossible exception of thenewData.hasChildren() rule, which would depend on whetherdeletions should be allowed.

Note: Validation rules are not meant to completely replace data validation codein your app. We recommend that you also perform input validation client-sidefor best performance and best user experience when your app is offline.

Query-based Rules

Although youcan't use rules as filters, youcan limit access to subsets of data by using query parameters in your rules.Usequery. expressions in your rules to grant read or write access based onquery parameters.

For example, the following query-based rule usesuser-based security rulesand query-based rules to restrict access to data in thebaskets collectionto only the shopping baskets the active user owns:

"baskets": {  ".read": "auth.uid !== null &&            query.orderByChild === 'owner' &&            query.equalTo === auth.uid" // restrict basket access to owner of basket}

The following query, which includes the query parameters in the rule, wouldsucceed:

db.ref("baskets").orderByChild("owner")                 .equalTo(auth.currentUser.uid)                 .on("value", cb)                 // Would succeed

However, queries that do not include the parameters in the rule would fail withaPermissionDenied error:

db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied

You can also use query-based rules to limit how much data a client downloadsthrough read operations.

For example, the following rule limits read access to only the first 1000results of a query, as ordered by priority:

messages: {  ".read": "query.orderByKey &&            query.limitToFirst <= 1000"}// Example queries:db.ref("messages").on("value", cb)                // Would fail with PermissionDenieddb.ref("messages").limitToFirst(1000)                  .on("value", cb)                // Would succeed (default order by key)

The followingquery. expressions are available in Realtime Database Security Rules.

Query-based rule expressions
ExpressionTypeDescription
query.orderByKey
query.orderByPriority
query.orderByValue
booleanTrue for queries ordered by key, priority, or value. False otherwise.
query.orderByChildstring
null
Use a string to represent the relative path to a child node. For example,query.orderByChild === "address/zip". If the query isn't ordered by a child node, this value is null.
query.startAt
query.endAt
query.equalTo
string
number
boolean
null
Retrieves the bounds of the executing query, or returns null if there is no bound set.
query.limitToFirst
query.limitToLast
number
null
Retrieves the limit on the executing query, or returns null if there is no limit set.

Next steps

After this discussion of conditions, you've got a more sophisticatedunderstanding ofSecurity Rules and are ready to:

Learn how to handle core use cases, and learn the workflow for developing,testing and deployingSecurity Rules:

LearnSecurity Rules features that are specific toRealtime Database:

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-02-18 UTC.