Firebase Realtime Database Rules
Realtime Database Security Rules control read and write access to your database. The rules are written as a JSON object mirroring your database structure. Each node in the tree can have its own read and write conditions.
Basic Rules Structure
{
"rules": {
".read": false,
".write": false,
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"posts": {
".read": true,
"$postId": {
".write": "auth !== null"
}
}
}
}
The $uid and $postId are wildcard variables that match any key at that level. Inside conditions, auth contains the authenticated user's data (auth.uid, auth.email).
Key Rule Variables
auth— authenticated user object (nullif not logged in)auth.uid— the user's unique ID$variable— wildcard capturing the key at that tree leveldata— current data at the matched pathnewData— data the client is trying to writenow— current server timestamp in milliseconds
Validating Incoming Data
{
"rules": {
"messages": {
"$messageId": {
".read": "auth !== null",
".write": "auth !== null",
".validate": "newData.hasChildren(['text', 'authorId', 'timestamp'])
&& newData.child('text').isString()
&& newData.child('text').val().length > 0
&& newData.child('text').val().length < 500
&& newData.child('authorId').val() === auth.uid"
}
}
}
}
The .validate rule runs additional checks on incoming data after .write passes. Use it to enforce data types, required fields, and value constraints.
Rules Cascade — Parent Rules Apply to Children
If a parent node grants read access, all its children inherit that access. You cannot restrict a child node if the parent already grants access. This differs from Firestore where document and collection rules are independent.
{
"rules": {
"publicData": {
".read": true, // entire subtree is readable
"secret": {
".read": false // ❌ This does NOT work — parent already granted access
}
}
}
}
Plan your tree structure so sensitive data sits in a separate top-level node with its own access restrictions.
Testing Rules in the Console
The Firebase Console has a Rules Simulator under Realtime Database > Rules. Enter a path, select an operation (read or write), enter a UID, and click Run. The simulator shows whether the rules allow or deny the operation and which rule caused the decision.
Key Takeaway
Realtime Database rules live in a JSON structure that mirrors your database tree. Use auth.uid and wildcard variables to restrict access per user. Validate incoming data with .validate rules. Remember that parent access grants cascade to all children — structure sensitive data at top-level nodes with their own restrictive rules.
Index Rules for Query Performance
Realtime Database rules include an .indexOn directive that tells Firebase which fields to index for efficient ordering and filtering. Without an index, queries that order by a child field perform a full scan:
{
"rules": {
"posts": {
".indexOn": ["publishedAt", "authorId", "likesCount"]
},
"users": {
".indexOn": ["email", "plan"]
}
}
}
Add an index for every field you use in orderByChild() queries. The Firebase Console warns you in logs when a query runs without an index and suggests the rule to add.
Using Now and Auth in Validate Rules
The now variable gives you the current server timestamp in milliseconds. Use it to enforce time-based rules, like preventing posts from being edited after 24 hours:
{
"rules": {
"posts": {
"$postId": {
".write": "auth !== null &&
(data.val() === null ||
(newData.child('authorId').val() === auth.uid &&
now - data.child('createdAt').val() < 86400000))"
}
}
}
}
86400000 is 24 hours in milliseconds. This rule allows write access only if the post was created less than 24 hours ago and the writer is the original author.
Deploying Realtime Database Rules
Store your rules in database.rules.json and deploy with:
firebase deploy --only database
Or deploy rules and Firestore rules together:
firebase deploy --only database,firestore:rules
The Firebase Console also lets you edit and publish rules directly in the browser under Realtime Database > Rules. Use the console for quick edits during development and the CLI for production deployments tracked in version control.
Simulating Rules
The Rules Simulator in the Realtime Database console lets you test read and write operations against your current rules. Enter a path, select the operation (read or write), enter auth data like a UID, and click Run Simulation. Firebase shows ALLOWED or DENIED and highlights the specific rule that made the decision.
Key Takeaway
Realtime Database rules use a JSON structure that mirrors your data tree. Use auth.uid and wildcard variables to control per-user access. Validate incoming data with .validate rules checking types, length, and required fields. Add .indexOn directives for every field used in ordering queries. Parent-level access grants cascade to all children — design your tree so sensitive data sits in separate top-level nodes. Always test rules in the simulator before deploying to production.
