Security6 min read

Is Your Firebase Database Publicly Readable? How to Check the Rules

Firebase config is public by design, so your Security Rules are the only thing between a stranger and your data. One request shows whether your Realtime Database answers anyone, and how to lock it down.

By UseShipReady

Your Firebase Realtime Database is publicly readable if its Security Rules grant .read to everyone, which is what test-mode rules and a top-level ".read": true do. Check by requesting https://YOUR-DB.firebaseio.com/.json?shallow=true in a private browser window. Permission denied means anonymous reads are blocked. A list of keys means anyone can read your data. Fix it by scoping rules to auth.uid, never to true.

Firebase apps work differently from a traditional backend. The browser talks to the database directly, and the Firebase config in your JavaScript, including the apiKey, is public by design. Firebase says so in its own documentation. That means there is no server of yours standing in the way: your Security Rules are the entire access control system. When they are open, the database is open.

Why do Firebase databases end up public?

Test-mode rules that never got replaced

When you create a database, the console offers "test mode", which lets anyone read and write so you can build without friction. In current versions it carries an expiry date. When that date arrives the app stops working, and the quickest "fix", often the one an assistant suggests, is to replace the expiring condition with plain true. The app works again, and the database is now public with no expiry.

json
{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Rules that only check for a signed-in user

".read": "auth != null" feels secure, but it means "any signed-in user can read everything". If your app allows open sign-up, or anonymous authentication is enabled, any stranger can meet that condition in seconds. It stops casual browsing, not someone who wants your data.

A grant high in the tree

Realtime Database rules cascade downward, and a grant cannot be taken back further down. If / or /users grants .read, a stricter rule on /users/$uid has no effect. This surprises people who expect the most specific rule to win, and it is why an app with carefully written per-user rules can still leak everything through one broad line at the top.

How do I check if my Firebase database is public?

Find your database URL in your Firebase config (databaseURL, ending in firebaseio.com or firebasedatabase.app) and request the root without any credentials:

shell
curl -s "https://YOUR-PROJECT-default-rtdb.firebaseio.com/.json?shallow=true"

shallow=true returns only the top-level key names, not the data under them, so the test itself does not download anything sensitive. {"error" : "Permission denied"} is the answer you want. Something like {"users":true,"orders":true} means the root is readable by anyone on the internet. Repeat the request for each top-level path your app uses, such as /users.json?shallow=true, because a locked root does not prove a child path is locked.

Check Cloud Storage too

Storage has its own rules, and allow read: if true on match /{allPaths=**} makes every uploaded file, such as profile photos, invoices and ID documents, readable and often listable. Review storage.rules with the same suspicion as your database rules.

What do secure Firebase Realtime Database rules look like?

Start from "deny everything" and open specific paths to specific users. The pattern for per-user data is to match the path segment to the signed-in user’s ID:

json
{
  "rules": {
    ".read": false,
    ".write": false,
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid === $uid",
        ".write": "auth != null && auth.uid === $uid"
      }
    },
    "publicPosts": {
      ".read": true,
      ".write": "auth != null",
      "$postId": {
        ".validate": "newData.hasChildren(['title', 'authorId']) && newData.child('authorId').val() === auth.uid"
      }
    }
  }
}
  • Keep the root at false and grant access as deep in the tree as possible, because grants cascade down and cannot be revoked below.
  • Use auth.uid === $uid for anything belonging to one user. auth != null alone is rarely the right condition for private data.
  • Add .validate rules so a signed-in user cannot write someone else’s ID or arbitrary fields.
  • Only make a path readable by everyone when the data is genuinely public, such as published posts or a product catalogue.
  • Test with the Rules Playground in the console or the Firebase Emulator Suite before deploying, then re-run the curl check against production.

Locking down Cloud Storage

Storage rules use a different syntax but the same idea: match the path to the owner. A common layout stores each user’s files under their ID, which makes the rule short and hard to get wrong:

javascript
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{allPaths=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Unlike Realtime Database rules, Storage rules do not cascade: access is denied unless some match block allows it. That makes the dangerous line easy to spot. Look for a broad match /{allPaths=**} with allow read and no condition on request.auth. If files genuinely need to be public, such as product images, give them their own folder with its own read rule rather than opening the whole bucket.

Tighten rules without breaking the app

  1. List every path your app reads and writes by searching the code for ref( and child( calls.
  2. Write a rule for each path in the emulator, and run your app against it until nothing is denied that should be allowed.
  3. Deploy the rules, then watch the Firebase console usage tab for a spike in denied requests over the next day.
  4. Only then remove the old broad grant, if you kept one as a fallback during testing.

Firebase App Check adds another layer by making it harder for scripts outside your app to call your backend, but it complements rules rather than replacing them. A request that passes App Check still has to pass your rules.

Is Firebase less secure than Supabase?

No. Both put a public identifier in the browser and rely on server-enforced rules for access control: Security Rules in Firebase, Row Level Security in Supabase. Both are secure when those rules are written carefully and exposed when they are not. The Supabase version of this article is is the Supabase anon key safe to expose, and the wider platform view is in Is Firebase secure?.

How UseShipReady checks Firebase

UseShipReady finds your Realtime Database URL and Storage bucket in the Firebase config your own JavaScript publishes, then asks each one the same unauthenticated question shown above. The database probe uses shallow=true, so only key names come back, never data. It reports a publicly readable Realtime Database and a publicly listable Storage bucket, both as critical.

Cloud Firestore is not covered. Testing it deterministically would mean guessing collection names, and a guess that misses proves nothing, so reporting "Firestore looks fine" would be a claim without evidence. If you use Firestore, review its rules in the console and test them in the emulator.

Frequently asked questions

Is the Firebase API key a secret?
No. Firebase documents the apiKey in your web config as an identifier that is safe to include in client code. Access to data is controlled by Security Rules, so that is where protection has to live.
What happens when Firebase test mode expires?
The time-limited rules start denying all reads and writes, so the app stops working. Replace them with rules scoped to authenticated users and their own data, not with a rule that allows everyone.
Does ".read": "auth != null" make my database secure?
Only partly. It blocks visitors who are not signed in, but any user who creates an account, or signs in anonymously if that is enabled, can read everything the rule covers. For private data, compare auth.uid to the owner of the record.
My database was public. Should I assume the data was copied?
Assume it may have been. Open databases are found by automated scans of Firebase config in public JavaScript. Lock the rules first, then check the usage graphs in the console for unexplained download spikes, and follow your legal obligations if the data included personal information.
Can a child rule override a parent rule in Realtime Database?
Not to remove access. Realtime Database rules cascade, so once a parent path grants read or write, every child path inherits it and a stricter child rule cannot revoke it.

Sources

Related checks

UseShipReady flags the issues in this article on a live site:

Related reading

Is your site ready to ship?

UseShipReady scans up to ten pages for security, AI exposure, email deliverability, SEO and launch readiness — with a paste-ready fix for each finding. Free, no signup.