Skip to content
SealMyApp

Firebase test mode expired or permission-denied errors: write real rules instead of reopening the database

When test-mode rules hit their expiry date, every client request is refused. Don't push the date forward: replace the rule with owner-based Firestore, Storage and Realtime Database rules, then test them.

Updated 2026-10-16

If your Firebase app suddenly fails with permission-denied (“Missing or insufficient permissions”), the usual reason is that your test-mode rules have reached their expiry date: they allowed everyone until that date, and now they allow no one. The right fix is to replace them with rules that let each signed-in user reach only their own data, not to push the date forward or switch to allow read, write: if true.

Firebase’s own quickstart describes test mode as allowing anyone to read and overwrite your data. Reopening it puts you back there, with your config sitting in your public JavaScript.

Is it test mode? Check your rules

Open the Rules tab for Firestore (or Storage, or Realtime Database) in the Firebase console. If you see a date check like this, you’re on test-mode rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.time < timestamp.date(2026, 10, 1);
    }
  }
}

Once the date passes, every client request is refused. If there’s no date and you still get errors, skip to rules are not filters.

Replace them with owner-based Firestore rules

Adjust the collection and field names to match your app. This covers a private users collection and a posts collection with public published posts:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Each user reads and writes only their own document
    match /users/{userId} {
      allow read, create, update, delete:
        if request.auth != null && request.auth.uid == userId;
    }

    // Anyone reads published posts; only the author writes
    match /posts/{postId} {
      allow read: if resource.data.published == true
        || (request.auth != null && request.auth.uid == resource.data.author_uid);
      allow create: if request.auth != null
        && request.resource.data.author_uid == request.auth.uid;
      allow update: if request.auth != null
        && resource.data.author_uid == request.auth.uid
        && request.resource.data.author_uid == request.auth.uid;
      allow delete: if request.auth != null
        && resource.data.author_uid == request.auth.uid;
    }

    // Everything else: no client access
  }
}

Notes:

  • resource.data is the document as it is now; request.resource.data is how it will look after the write. Checking both on update stops someone handing a post to another user.
  • Anything not matched is denied, so you don’t need a catch-all deny rule. Remove the old match /{document=**} block entirely.
  • Admin-only data: Firebase’s docs show checking a role with get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true. Make sure users can’t write that admin field on their own document, for example by blocking it in the update rule or setting roles only from your server.

Storage rules

Store each user’s files under users/<uid>/:

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

Realtime Database rules

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

Still denied? Rules are not filters

Firebase’s query docs say security rules are not filters: if a query could return a document the user isn’t allowed to read, the whole query fails, even if every actual result would be allowed. With the rules above:

// Fails: could include other people's drafts
getDocs(collection(db, "posts"));

// Works: every possible result passes the rule
getDocs(query(collection(db, "posts"), where("published", "==", true)));
getDocs(query(collection(db, "posts"), where("author_uid", "==", auth.currentUser.uid)));

Two other common causes:

  • The user isn’t signed in yet. A query that runs before Firebase Auth has restored the session goes out with no request.auth. Wait for onAuthStateChanged before querying.
  • The field name differs. If documents store userId but the rule checks author_uid, nothing matches.

Deploy and test

  • Console: paste the rules into the Rules tab and click Publish. Firebase says changes can take up to a minute for new queries and up to 10 minutes for active listeners.
  • CLI: keep the rules in your project (for example firestore.rules) and run firebase deploy --only firestore. Firebase notes a CLI deploy overwrites rules edited in the console, so keep one source of truth.
  • Test before publishing: the console’s Rules Playground simulates a read or write as a signed-in or signed-out user. The Local Emulator Suite runs your rules locally for automated tests.
  • Test the important cases both ways: user A can read their own document, user A can’t read user B’s, a signed-out visitor can’t read either.

Prompt for your AI builder

My Firebase test-mode rules have expired. Do NOT extend the date and do NOT use
"allow read, write: if true". Write Firestore and Storage rules where:
- users/{userId}: only the signed-in user with that uid can read or write.
- posts: anyone reads documents with published == true; only the author
  (author_uid == request.auth.uid) can create, update or delete, and updates
  can't change author_uid.
- storage users/{userId}/...: only that user reads and writes.
- everything else is denied.
Also update my queries so they filter by author_uid or published, because rules
are not filters. Show me the rules and the changed queries.

FAQ

Can't I just move the test-mode date forward?

You can, and it will make the errors go away, but until that date anyone who finds your Firebase config (it's public in your app) can read and overwrite your whole database. Firebase's quickstart describes test mode as allowing anyone to read and overwrite your data.

Is allow read, write: if request.auth != null good enough?

Usually not. Firebase's docs warn that it lets any signed-in user reach the data, and signing up is normally open to anyone. Compare request.auth.uid with the document's owner instead.

I published new rules but still get permission errors.

Firebase says rule changes can take up to a minute to affect new queries and up to 10 minutes for active listeners. If it persists, the query probably doesn't match the rule: add a where filter on the owner field, because rules are not filters.

Does the server (Admin SDK) need these rules?

No. Firebase's quickstart says production-mode rules deny mobile and web clients while your authenticated server applications can still access the database. Security Rules apply to client requests.

Should I hide my Firebase API key?

No need: the Firebase web config, including the API key, is meant to be public. Your rules are what protect the data. More in our Firebase security rules guide.

Check it for free

After publishing, check what a stranger can still read. Our free scan tests your live app’s Firestore, Realtime Database and Storage for open reads from the outside, after you verify you own the app, and keeps no data. Run a free scan. For more rule patterns see Firebase security rules for vibe-coded apps, and for everything else before launch, the vibe coding security checklist. Our key checker confirms which keys are meant to be public.

Sealed — a short weekly security note for vibe coders

One common security mistake in AI-built apps, how to fix it in your builder, and one useful tool. No spam; unsubscribe any time.