HomeGuidesFirebase Storage rules: fix allow read, write: if true
Guide
Firebase Storage rules: fix allow read, write: if true
What Firebase Storage test mode and open rules allow, how to find the rules your bucket really runs, and owner only upload rules with size and type limits.
Independent notes from VibeLocked. Not a pentest, and not affiliated with the tools named below.
Your app lets people upload a profile photo, a receipt, or a PDF. The upload failed with storage/unauthorized, so you or your AI agent changed the Storage rules to allow read, write: if true; and it worked. It also means anyone on the internet can now list, download, overwrite, and delete every file in your bucket, and fill it with their own.
This guide is only about Cloud Storage for Firebase: test mode and its expiry, finding the rules your bucket actually runs, owner only upload rules with size and type limits, public folders that do not leak a file list, Firestore lookups inside Storage rules, and download URL tokens. For the same mistake in your database, read Firestore allow read, write: if true. If you use Supabase instead, see Supabase Storage bucket security, and for upload tokens, Supabase signed upload URLs and list policies.
Checked against the Firebase and Google Cloud docs linked below on October 26, 2026. VibeLocked is independent and not affiliated with Google or Firebase.
The short version
- Storage has its own rules, separate from Firestore. Fixing
firestore.rulesdoes nothing for your files. allow read, write;,if true, andif request.time < timestamp.date(...)on/{allPaths=**}all mean the bucket is open to anyone.- Put each user's files under a path with their uid and compare it to
request.auth.uid. - Limit uploads with
request.resource.sizeand acontentType.matches()check onrequest.resource. - For public files, prefer
allow getoverallow read, so strangers cannot list the folder. - A download URL works for anyone who has it. Rules do not stop it.
- App Check is a second layer, not a replacement for rules.
What test mode and open rules actually allow
When you create a bucket in the console you pick Locked mode or Test mode. Firebase's basic rules page says Test mode allows anyone access. The Test mode template sets a date about 30 days out. The comment the console writes above it, reproduced in tutorials such as this Flutter setup guide, says the rule lets anyone with your bucket reference view, edit, and delete all data, and that it is configured to expire after 30 days, after which all client requests to the bucket are denied:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.time < timestamp.date(2026, 11, 25);
}
}
}
So you get two bad outcomes. Until the date, the bucket is wide open. After it, uploads and downloads in your app fail, and the quick "fix" an agent reaches for is if true or a later date.
The fully open version is worse because it never expires. Firebase's insecure rules page shows it with the warning that it makes every file in your bucket accessible to any user, even people who do not use your app, and that anyone can overwrite all your files:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
{allPaths=**} matches every file. A bare allow read, write; or if true has no condition. On a legacy *.appspot.com default bucket there is one more catch: Firebase's Google Cloud integration page says the bucket is shared with App Engine, so public rules also make newly uploaded App Engine files public.
Why a stranger can reach your bucket at all: your firebaseConfig, including storageBucket, ships in your JavaScript by design. Our guide on why the Firebase API key is public explains why that is fine. Firebase's insecure rules page is blunt about the rest: if you are not authenticating users and configuring rules, anyone who guesses your project ID can steal, modify, or delete the data. The rules are the lock.
"Any signed in user" is not much better
allow read, write: if request.auth != null; on /{allPaths=**} is the other common shortcut. The insecure rules page lists it as not recommended. If sign up is open, anyone can create an account in your app, or sign in anonymously if you enabled that, and then read or replace every other user's files. That is the file version of IDOR.
Find the rules your bucket really runs
- Console: open your project, go to Storage, then the Rules tab. Firebase's Storage rules get started page says the console always shows the most recently deployed rules, so trust it over the file in your repo.
- Repo: open
firebase.json. The Firebase CLI docs show the Storage entry as"storage": { "rules": "storage.rules" }. That file is whatfirebase deployuploads. If your repo has nostorage.rules, the console is the only copy. - Search both for
if true,allow read, write;with no condition,request.time <, andrequest.auth != nullon/{allPaths=**}. - Look for overlapping matches. The Storage core syntax page says that when more than one
allowmatches a file, access is allowed if any condition is true. A strict rule on/users/{userId}/{fileName}does nothing while a catch all/{allPaths=**}withif truesits below it. - Rules Playground: in the Rules tab, click Rules Playground. The Playground page lets you simulate a read or write at a path as unauthenticated, anonymous, or a specific user ID. Try a
getonusers/someone-else/photo.jpgsigned out, then as a different uid. Both should be denied.
If the Rules tab shows allow read, write: if false;, strangers are locked out, and so is your app. That is the Locked mode default in the basic rules page and the starting storage.rules that firebase init storage writes, as the firebase-tools template shows.
Owner only rules for user uploads
Storage has no folders, only file names with slashes, but rules match those names like paths. Build every upload path from the signed in user's uid, for example users/{uid}/receipts/2026-10.pdf, and check it. Firebase's basic rules page calls this content owner only access. This version adds a size cap and an explicit list of types:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Private files: users/<uid>/...
match /users/{userId}/{allPaths=**} {
allow read, delete: if request.auth != null
&& request.auth.uid == userId;
allow create, update: if request.auth != null
&& request.auth.uid == userId
&& request.resource.size < 5 * 1024 * 1024
&& request.resource.contentType.matches('image/(png|jpeg|webp)');
}
}
}
How each part works, per the Storage conditions page and core syntax page:
request.authisnullfor signed out requests. When a user is signed in it holds theiruidand token claims.{userId}captures the first path segment afterusers/, sorequest.auth.uid == userIdpins each user to their own prefix.rules_version = '2'makes{allPaths=**}match zero or more segments, so it coversusers/<uid>/photo.jpgandusers/<uid>/receipts/a.pdf. It is also required forlist.readcoversgetandlist.writecoverscreate,update, anddelete. Delete is split out above so the size and type checks onrequest.resourceapply only to uploads and metadata changes, while the owner can always remove their own file.request.resourceis the file about to be written.request.resource.sizeis in bytes.
Set the uid in the path from your auth state, never from a form field or URL parameter. In the browser that means building the reference from auth.currentUser.uid, so the rule and the code agree.
Size and type limits, and what they do not prove
Without a size cap, any signed in user can upload very large files to your bucket, and you pay for storing and serving them. Firebase's conditions page shows the 5 MB image example this rule builds on.
contentType is metadata, not a scan of the file. Firebase's upload files page says Cloud Storage infers the type from the file extension, and that a contentType you set in the upload metadata overrides it. So the rule checks what the client claims. That still blocks honest mistakes and casual abuse. It does not prove the bytes are a real image.
Two practical points:
matches()compares the whole string, so'image/(png|jpeg|webp)'rejectsimage/png; charset=xstyle tricks and anything else not on the list.- Avoid
image/.*. It also acceptsimage/svg+xml, and SVG can carry script. If you must accept SVG or HTML, read the upload advice in secure file uploads in Next.js first. Its points about allow lists and never trusting the browser'sfile.typeapply to Firebase too.
If you need stronger checks, such as reading the real file type or stripping image metadata, let users upload to a private path and run that processing on a server you control before moving the file to its final location. If that server is a Cloud Function, it uses the Admin SDK, which ignores these rules, so the function must check who is calling itself; see Firebase Cloud Functions auth.
Public files without a public bucket
Some files really are public: avatars, product photos, a logo. Firebase's basic rules page calls this mixed public and private access: anyone reads, only the owner writes. Give public files their own path, and keep everything else private:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Public avatars: anyone can view one file, only the owner can change it
match /avatars/{userId}/{fileName} {
allow get;
allow create, update: if request.auth != null
&& request.auth.uid == userId
&& request.resource.size < 2 * 1024 * 1024
&& request.resource.contentType.matches('image/(png|jpeg|webp)');
allow delete: if request.auth != null && request.auth.uid == userId;
}
// Private files stay owner only (see the rules above)
match /users/{userId}/{allPaths=**} {
allow read, delete: if request.auth != null && request.auth.uid == userId;
allow create, update: if request.auth != null
&& request.auth.uid == userId
&& request.resource.size < 5 * 1024 * 1024
&& request.resource.contentType.matches('image/(png|jpeg|webp)');
}
}
}
Note allow get instead of allow read. Per the core syntax page, read includes list, so allow read; on a public folder also lets a stranger list every file name in it. With get alone, someone needs the exact path to fetch a file.
Anything you do not match is denied. The core syntax page says users can only access files your rules specifically allow, and Storage rules do not cascade. You do not need a catch all deny rule. You need to not have a catch all allow rule.
Team access with Firestore lookups
If files belong to a team or project rather than one user, Storage rules can read Firestore. The Storage conditions page documents firestore.get() and firestore.exists(), with fully specified document paths and variables written as $(variable):
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /teams/{teamId}/{allPaths=**} {
allow read, write: if request.auth != null
&& firestore.exists(
/databases/(default)/documents/teams/$(teamId)/members/$(request.auth.uid)
);
}
}
}
Limits from the same page: each read counts toward your Firestore usage and billing, a single rules evaluation may access no more than two Firestore documents, and Storage rules can only read the default Firestore database. The first time you deploy rules that use these functions, the console or CLI prompts you to grant the permission that connects the two products. The manage and deploy page explains how to remove it later. Your Firestore rules must also stop users from adding themselves to a team's members collection, or this check means nothing.
For a few fixed roles, custom claims in request.auth.token avoid the extra reads. The basic rules page shows that pattern for Storage. A claim only changes when the user's ID token refreshes, so a removed admin still passes a claim check in rules until then; Firebase custom claims for admin roles has our emulator results and the fix.
Download URLs are bearer links
This is the part rules do not cover. getDownloadURL() returns a URL with a token parameter, and the download files page shows it being put straight into an img tag. Firebase's Admin SDK Storage page describes the same kind of link as a non-expiring download URL and says anyone with this URL can permanently access the file. The person holding the link does not need to sign in.
What that means for you:
- Your rules control requests made through the Firebase SDK. Once a download URL exists, whoever holds it can use it until you revoke the token.
- Do not save download URLs for private files in a Firestore document that other users can read, in logs, or in emails you do not control.
- For private files, download with
getBlob()orgetBytes(). The download files page says these return data in your code and allow finer grained access control through Security Rules. - If a URL leaks, Firebase's manage stored files page says to open the file in the console, expand File location, click Create new download URL, then revoke the old one.
- If you need a link that expires, Google Cloud's signed URLs give time limited access. They are created on a server, usually with a service account, and anyone holding one can use it until it expires, so keep the expiry short.
Deploy the fix without undoing it
Console: paste the rules into the Storage Rules tab, test in the Playground, click Publish.
CLI: put the rules in storage.rules and run:
firebase deploy --only storage
Pick one place to edit. The manage and deploy page warns that a CLI deploy overwrites the rules in the console, so a console fix can be undone by the next deploy of an old storage.rules. If you fixed it in the console, copy the same rules into the file today. The same page says rule releases can take several minutes to fully propagate.
Test rules with the emulator
The Playground is fine for a quick check. For rules you will keep changing, or that an AI agent may edit, add tests. The emulator setup page starts the Storage emulator with firebase emulators:start --only storage, and the rules unit testing page documents @firebase/rules-unit-testing: authenticatedContext("alice") and unauthenticatedContext() give you a signed in or signed out client, ctx.storage() returns a Storage instance for it, and assertSucceeds and assertFails check the result. The library talks only to the emulators and never touches production.
Write these for each private path at minimum:
- Signed out
getofusers/alice/a.pngfails. - Alice can upload a small PNG to
users/alice/and read it back. - Bob cannot read, overwrite, or delete Alice's file.
- Alice cannot upload a 6 MB file, or a file with content type
text/html. - A signed out user can
geta public avatar but cannotlisttheavatars/folder.
Run the suite in CI so a rules change that opens access fails the build before it ships.
App Check is the second layer
App Check checks that requests come from your registered app. The enforcement page lists Cloud Storage among the products you can enforce, says all unverified requests are rejected once you do, and notes it can take up to 15 minutes to take effect. Watch the metrics first so real users on old app versions are not blocked.
Firebase describes App Check as protecting you, the developer, while Authentication protects your users, and says the two are complementary. App Check does not know who owns a file. Someone using your real app is still limited only by your rules.
If your bucket was open
- Publish owner only rules today and confirm in the Playground that signed out reads of private paths are denied
- Remove every
if true, bareallow read, write;, andrequest.timerule, including catch alls - Put the same rules in
storage.rulesso the next deploy does not undo the fix - Look through the Files tab for files you did not upload, such as HTML pages, scripts, or very large files, and delete them
- For private files whose download URLs may have spread, create new download URLs and revoke the old ones
- Check your Firestore and Realtime Database rules too, with the Firebase security rules checklist
- If private user files could have been read, treat it as a possible data breach and check what you owe your users under the privacy laws that apply to you
- Turn on App Check enforcement for Storage once metrics look clean
Common AI generated mistakes
- "Fixing"
storage/unauthorizedwithif true. The error usually means your rules or your path are wrong, for example uploading touploads/photo.jpgwhen the rule expectsusers/<uid>/.... Fix the path, not the rule. - Taking the uid from the request body or URL. The rule only protects you if the path segment is checked against
request.auth.uid. - Leaving the template catch all in place below new specific rules. Any matching
allowwins. allow readon a public folder whenallow getwas enough, which hands out a file list.- Storing download URLs of private files next to public data.
- Editing in the console and deploying an old file from the repo afterward.
Copy-paste prompt for your AI coding tool
Paste this into Cursor, Claude Code, Codex, Lovable, Bolt, or Replit inside your own project. It asks for file names, paths, and rules only. Do not paste service account keys or any other secret into the chat.
This project uses Cloud Storage for Firebase. Do not print, log, or ask me for any secret values. 1) Find firebase.json and the storage.rules file it points to, and show me the current Storage rules. Tell me if there is no rules file in the repo. 2) Flag any rule that uses if true, a bare allow read, write;, request.time <, or request.auth != null on a broad path such as /{allPaths=**}, and any catch all match that would override stricter rules. 3) List every place the app uploads or downloads files (uploadBytes, uploadBytesResumable, uploadString, getDownloadURL, getBlob, getBytes, deleteObject) and the path each one uses. Say whether the path includes the signed in user's uid from Firebase Auth, and flag any path built from a form field or URL parameter. 4) Propose rules_version 2 Storage rules: owner only paths under users/{userId} checked against request.auth.uid, separate allow get rules for any truly public folder, delete split from create and update, request.resource.size limits, and request.resource.contentType.matches with an explicit list of types. 5) Flag any place that stores getDownloadURL results for private files somewhere other users can read. 6) Write @firebase/rules-unit-testing tests for the Storage emulator covering signed out access, owner access, another user's access, oversized files, and wrong content types. Do not deploy anything or change files until I approve the plan.
Deploy the rules yourself after you have read them. More prompts are in the security review prompt guide.
Check what your live app exposes with LeakCheck
LeakCheck scans any live app URL or public GitHub repo, read only, wherever it is hosted. On a live app it reads the Firebase config your site ships to visitors and runs the kind of requests a stranger could make without signing in, alongside its other checks for exposed files and leaked keys.
Its limits, honestly: it cannot see your rules file, it does not upload files to test write access, and it cannot tell whether someone already downloaded something. A clean scan is not proof your Storage rules are right. The Playground and emulator tests above are how you prove that.
Sources
- Firebase: Get started with Security Rules for Cloud Storage
- Firebase: Core syntax of Security Rules for Cloud Storage
- Firebase: Use conditions in Cloud Storage Security Rules
- Firebase: Basic Security Rules (Locked mode and Test mode)
- Firebase: Avoid insecure rules
- Firebase: Get started with Cloud Storage on web
- Firebase: Upload files with Cloud Storage on web
- Firebase: Download files with Cloud Storage on web
- Firebase: Get started with Cloud Storage in the Admin SDK (shareable download URLs)
- Firebase: Manage stored files in the console
- Firebase: Integrate with Google Cloud (legacy App Engine buckets)
- Firebase: Rules language reference, String.matches
- Firebase: Manage and deploy Firebase Security Rules
- Firebase: Firebase CLI reference (firebase.json)
- Firebase: Quickly validate Security Rules (Rules Playground)
- Firebase: Set up the emulator for Security Rules
- Firebase: Build unit tests for Security Rules
- Firebase: App Check overview
- Firebase: Enable App Check enforcement
- Google Cloud: Signed URLs
- firebase-tools: default storage.rules template
- DBestech: Flutter multi vendor app setup, showing the Storage test mode rule and its 30 day comment
Questions
Is allow read, write: if true safe in Firebase Storage?
No. Firebase's insecure rules page says an open Storage ruleset makes every file in the bucket accessible to any user and means anyone can overwrite all your files. That includes people who never use your app. Use owner checks on a path that contains the user's uid instead.
What happens when Firebase Storage test mode expires?
Test mode is a rule like allow read, write: if request.time < timestamp.date(...). Until that date anyone can read, overwrite, and delete files. After it, every client request to the bucket is denied and uploads in your app start failing. Write real rules before the date. Do not move the date forward or switch to if true.
Do Firebase Storage rules protect download URLs?
Not after the URL exists. A URL from getDownloadURL carries a download token. Firebase describes these as non-expiring download URLs and says anyone with the URL can permanently access the file. Treat it like a password. If one leaks, create a new download URL in the console and revoke the old one. For private files, download with getBlob or getBytes, which go through your rules.
Does App Check replace Storage security rules?
No. Once you enforce App Check for Cloud Storage, requests without a valid App Check token are rejected, but App Check does not know which user owns which file. Firebase describes App Check and Authentication as complementary. Rules that compare request.auth.uid to the file path still do the access control.