Learn the core syntax of the Realtime Database Security Rules language Stay organized with collections Save and categorize content based on your preferences.
Firebase Realtime Database Security Rules allow you to control access to data storedin your database. The flexible rules syntax allows you to createrules that match anything, from all writes to your database to operationson individual nodes.
Realtime Database Security Rules aredeclarative configuration for your database. This meansthat the rules are defined separately from the product logic. Thishas a number of advantages: clients aren't responsible for enforcing security, buggyimplementations will not compromise your data, and perhaps most importantly,there is no need for an intermediate referee, such as a server, to protect datafrom the world.
This topic describes the basic syntax and structure Realtime Database Security Rulesused to create complete rulesets.
Structuring Your Security Rules
Realtime Database Security Rules are made up of JavaScript-like expressions contained in aJSON document. The structure of your rules should follow the structure of thedata you have stored in your database.
Basic rulesidentify a set of nodes to be secured, theaccess methods (e.g., read,write) involved, andconditions under which access is either allowed or denied.In the following examples, ourconditions will be simpletrue andfalse statements, but in the next topic we'll cover more dynamic ways toexpress conditions.
So, for example, if we are trying to secure achild_node under aparent_node,the general syntax to follow is:
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } }}Let's apply this pattern. For example, let's say you are keeping track of a listof messages and have data that looks like this:
{ "messages": { "message0": { "content": "Hello", "timestamp": 1405704370369 }, "message1": { "content": "Goodbye", "timestamp": 1405704395231 }, ... }}Your rules should be structured in a similar manner. Here's a set ofrules for read-only security that might make sense for this data structure. Thisexample illustrates how we specify database nodes to which rules apply and theconditions for evaluating rules at those nodes.
{"rules":{//Forrequeststoaccessthe'messages'node..."messages":{//...andtheindividualwildcarded'message'nodesbeneath//(we'll cover wildcarding variables more a bit later)...."$message":{//Foreachmessage,allowareadoperationif<condition>.Inthis//case,wespecifyourconditionas"true",soreadaccessisalwaysgranted.".read":"true",//Forread-onlybehavior,wespecifythatforwriteoperations,our//conditionisfalse.".write":"false"}}}}
Basic Rules Operations
There are three types of rules for enforcing security based on the type ofoperation being performed on the data:.write,.read, and.validate. Hereis a quick summary of their purposes:
| Rule Types | |
|---|---|
| .read | Describes if and when data is allowed to be read by users. |
| .write | Describes if and when data is allowed to be written. |
| .validate | Defines what a correctly formatted value will look like, whether it has child attributes, and the data type. |
.write or.readrule is specified at or above a path, access will be denied.Wildcard Capture Variables
All rules statements point to nodes. A statement can point to a specificnode or use$ wildcardcapture variables to point to sets of nodes at alevel of the hierarchy. Use these capture variables to store the value of nodekeys for use inside subsequent rules statements. This technique lets you writemore complexSecurity Rulesconditions, something we'll cover in more detailin the next topic.
{"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 } } }}$ variable to a number,this will always fail. This canbe corrected by converting the number to a string (e.g.$key === newData.val()+'')Read and Write Rules Cascade
Note: Shallower security rules override rules at deeper paths. Child rules can only grantadditional privileges to what parent nodes have already declared. They cannot revoke aread or write privilege..read and.write rules work from top-down, with shallowerrules overriding deeper rules. If a rule grants read or write permissions at a particularpath, then it also grants access toall child nodes under it. Consider the following structure:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } }}This security structure allows/bar/ to be read from whenever/foo/ contains a childbaz with valuetrue.The".read": false rule under/foo/bar/ has noeffect here, since access cannot be revoked by a child path.
While it may not seem immediately intuitive, this is a powerful part of the rules languageand allows for very complex access privileges to be implemented with minimal effort. Thiswill be illustrated when we get into user-based security later in this guide.
Note that.validate rules do not cascade. All validate rulesmust be satisfied at all levels of the hierarchy in order for a write to be allowed.
Rules Are Not Filters
Rules are applied in an atomic manner. That means that a read or writeoperation is failed immediately if there isn't a rule at that location or at aparent location that grants access. Even if every affected child path is accessible,reading at the parent location will fail completely. Consider this structure:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } }}Without understanding that rules are evaluated atomically, it might seemlike fetching the/records/ path would returnrec1but notrec2. The actual result, however, is an error:
JavaScript
vardb=firebase.database();db.ref("records").once("value",function(snap){// success method is not called},function(err){// error callback triggered with PERMISSION_DENIED});
Objective-C
FIRDatabaseReference*ref=[[FIRDatabasedatabase]reference];[[_refchild:@"records"]observeSingleEventOfType:FIRDataEventTypeValuewithBlock:^(FIRDataSnapshot*snapshot){// success block is not called}withCancelBlock:^(NSError*_Nonnullerror){// cancel block triggered with PERMISSION_DENIED}];
Swift
varref=FIRDatabase.database().reference()ref.child("records").observeSingleEventOfType(.Value,withBlock:{snapshotin// success block is not called},withCancelBlock:{errorin// cancel block triggered with PERMISSION_DENIED})
Java
FirebaseDatabasedatabase=FirebaseDatabase.getInstance();DatabaseReferenceref=database.getReference("records");ref.addListenerForSingleValueEvent(newValueEventListener(){@OverridepublicvoidonDataChange(DataSnapshotsnapshot){// success method is not called}@OverridepublicvoidonCancelled(FirebaseErrorfirebaseError){// error callback triggered with PERMISSION_DENIED});});
REST
curl https://docs-examples.firebaseio.com/rest/records/# response returns a PERMISSION_DENIED error
Since the read operation at/records/ is atomic, and there's noread rule that grants access to all of the data under/records/,this will throw aPERMISSION_DENIED error. If we evaluate thisrule in the security simulator in ourFirebase console, we can see thatthe read operation was denied because no read rule allowed access to the/records/ path. However, note that the rule forrec1was never evaluated because it wasn't in the path we requested. To fetchrec1, we would need to access it directly:
JavaScript
vardb=firebase.database();db.ref("records/rec1").once("value",function(snap){// SUCCESS!},function(err){// error callback is not called});
Objective-C
FIRDatabaseReference*ref=[[FIRDatabasedatabase]reference];[[refchild:@"records/rec1"]observeSingleEventOfType:FEventTypeValuewithBlock:^(FIRDataSnapshot*snapshot){// SUCCESS!}];
Swift
varref=FIRDatabase.database().reference()ref.child("records/rec1").observeSingleEventOfType(.Value,withBlock:{snapshotin// SUCCESS!})
Java
FirebaseDatabasedatabase=FirebaseDatabase.getInstance();DatabaseReferenceref=database.getReference("records/rec1");ref.addListenerForSingleValueEvent(newValueEventListener(){@OverridepublicvoidonDataChange(DataSnapshotsnapshot){// SUCCESS!}@OverridepublicvoidonCancelled(FirebaseErrorfirebaseError){// error callback is not called}});
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1# SUCCESS!
Overlapping Statements
It's possible for a more than one rule to apply to a node. In thecase where multiple rules expressions identify a node, the access method isdenied ifany of the conditions isfalse:
{ "rules": { "messages": { // A rule expression that applies to all nodes in the 'messages' node "$message": { ".read": "true", ".write": "true" }, // A second rule expression applying specifically to the 'message1` node "message1": { ".read": "false", ".write": "false" } } }}In the example above, reads to themessage1 node will bedenied because the second rules is alwaysfalse, even though the firstrule is alwaystrue.
Next steps
You can deepen your understanding of Firebase Realtime Database Security Rules:
Learn the next major concept of theSecurity Rules language, dynamicconditions, which let yourSecurity Rules check userauthorization, compare existing and incoming data, validate incoming data, checkthe structure of queries coming from the client, and more.
Review typical security use cases and theFirebase Security Rules definitions that address them.
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.