Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Restrict Access to Firebase Storage Files

Restrict access to Firebase Storage files using security rules that check request.auth for authenticated users, validate file metadata like size and content type, and scope file paths to user IDs. Storage security rules are separate from Firestore rules and use their own syntax with match patterns on file paths. The most common pattern grants each user read/write access only to files under their own UID folder, preventing unauthorized access to other users' uploads.

What you'll learn

  • How to write Firebase Storage security rules for authentication-based access
  • How to restrict uploads by file size and content type
  • How to scope file access to individual users using UID-based paths
  • How to set up public read with private write for shared resources
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read15-20 minFirebase Storage (Spark and Blaze plans), Firebase JS SDK v9+March 2026RapidDev Engineering Team
TL;DR

Restrict access to Firebase Storage files using security rules that check request.auth for authenticated users, validate file metadata like size and content type, and scope file paths to user IDs. Storage security rules are separate from Firestore rules and use their own syntax with match patterns on file paths. The most common pattern grants each user read/write access only to files under their own UID folder, preventing unauthorized access to other users' uploads.

Securing Firebase Storage Files with Security Rules

Firebase Storage uses security rules to control who can upload, download, and delete files. By default, new projects have rules that deny all access. This tutorial shows how to write rules that authenticate users, validate uploads, restrict access by user, and set up different access levels for public and private files. All rules are defined in storage.rules and deployed with the Firebase CLI.

Prerequisites

  • A Firebase project with Storage enabled
  • Firebase CLI installed (npm install -g firebase-tools)
  • Firebase Authentication set up with at least one sign-in method
  • Basic understanding of Firebase security rules syntax

Step-by-step guide

1

Understand the default Storage rules

When you enable Firebase Storage, the default rules deny all read and write access. This is the most secure starting point. You must explicitly grant access in your rules. Storage rules use match patterns on file paths and allow/deny based on conditions. The request object contains auth information and the resource being accessed.

typescript
1rules_version = '2';
2service firebase.storage {
3 match /b/{bucket}/o {
4 // Default: deny all access
5 match /{allPaths=**} {
6 allow read, write: if false;
7 }
8 }
9}

Expected result: All read and write operations are denied. Your app will show 'storage/unauthorized' errors until you add specific rules.

2

Allow authenticated users to read and write

The most basic useful rule checks request.auth to ensure a user is signed in. This prevents anonymous access while allowing any authenticated user to read and write files. Use request.auth != null as the condition. You can further restrict by checking request.auth.uid to limit access to specific users.

typescript
1rules_version = '2';
2service firebase.storage {
3 match /b/{bucket}/o {
4 // Only authenticated users can read or write
5 match /{allPaths=**} {
6 allow read, write: if request.auth != null;
7 }
8 }
9}

Expected result: Signed-in users can upload and download any file. Unauthenticated requests are rejected with a permission error.

3

Scope file access to individual users

The most common pattern creates a folder per user using their UID and restricts each user to their own folder. The match pattern captures the userId from the path, and the rule compares it to request.auth.uid. This ensures users can only access their own files. Structure your client-side upload paths as users/{uid}/filename to match this rule.

typescript
1rules_version = '2';
2service firebase.storage {
3 match /b/{bucket}/o {
4 // Each user can only access their own folder
5 match /users/{userId}/{allPaths=**} {
6 allow read, write: if request.auth != null
7 && request.auth.uid == userId;
8 }
9
10 // Public assets readable by anyone, writable by admins
11 match /public/{allPaths=**} {
12 allow read: if true;
13 allow write: if request.auth != null
14 && request.auth.token.admin == true;
15 }
16 }
17}

Expected result: Each user can only read and write files in their own users/{uid}/ folder. Public assets are readable by everyone.

4

Validate file size and content type on upload

Use request.resource to validate the file being uploaded. Check request.resource.size to enforce a maximum file size and request.resource.contentType to restrict allowed file types. These validations happen server-side and cannot be bypassed by the client. Combine size and type checks with authentication for comprehensive upload validation.

typescript
1rules_version = '2';
2service firebase.storage {
3 match /b/{bucket}/o {
4 match /users/{userId}/avatar.{ext} {
5 // Only the file owner can upload their avatar
6 // Max 5MB, images only
7 allow read: if request.auth != null;
8 allow write: if request.auth != null
9 && request.auth.uid == userId
10 && request.resource.size < 5 * 1024 * 1024
11 && request.resource.contentType.matches('image/.*');
12 }
13
14 match /users/{userId}/documents/{document} {
15 // Max 10MB, PDFs and images only
16 allow read: if request.auth != null
17 && request.auth.uid == userId;
18 allow write: if request.auth != null
19 && request.auth.uid == userId
20 && request.resource.size < 10 * 1024 * 1024
21 && (request.resource.contentType == 'application/pdf'
22 || request.resource.contentType.matches('image/.*'));
23 }
24 }
25}

Expected result: Uploads exceeding the size limit or with disallowed content types are rejected. Valid uploads from the file owner succeed.

5

Deploy Storage rules with the Firebase CLI

Save your rules in the storage.rules file in your project root. Then deploy them using the Firebase CLI. Rules take effect within a few seconds for new operations and up to 10 minutes for active connections. Always test rules in the Firebase Console's Rules Playground before deploying to production.

typescript
1# Deploy only storage rules
2firebase deploy --only storage
3
4# Or deploy everything
5firebase deploy

Expected result: The CLI confirms the rules were deployed successfully. New operations immediately follow the updated rules.

6

Upload files with matching client-side paths

Your client-side upload code must use paths that match your security rules. If your rules expect files under users/{userId}/, your upload code must construct paths using the authenticated user's UID. Use ref() to create a reference to the target path and uploadBytes() or uploadBytesResumable() to upload the file.

typescript
1import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage'
2import { getAuth } from 'firebase/auth'
3
4const storage = getStorage()
5const auth = getAuth()
6
7async function uploadAvatar(file: File) {
8 const user = auth.currentUser
9 if (!user) throw new Error('Must be signed in to upload.')
10
11 // Path must match the security rule pattern
12 const avatarRef = ref(storage, `users/${user.uid}/avatar.jpg`)
13
14 try {
15 const snapshot = await uploadBytes(avatarRef, file, {
16 contentType: file.type
17 })
18 const downloadUrl = await getDownloadURL(snapshot.ref)
19 console.log('Avatar uploaded:', downloadUrl)
20 return downloadUrl
21 } catch (error: any) {
22 if (error.code === 'storage/unauthorized') {
23 console.error('Upload rejected by security rules.')
24 } else {
25 console.error('Upload failed:', error.message)
26 }
27 throw error
28 }
29}

Expected result: The avatar is uploaded to the user's folder and a download URL is returned. Unauthorized uploads are rejected with a clear error.

Complete working example

storage.rules
1rules_version = '2';
2service firebase.storage {
3 match /b/{bucket}/o {
4
5 // User-scoped files: only the owner can read/write
6 match /users/{userId}/{allPaths=**} {
7 allow read: if request.auth != null
8 && request.auth.uid == userId;
9 allow write: if request.auth != null
10 && request.auth.uid == userId
11 && request.resource.size < 10 * 1024 * 1024;
12 }
13
14 // User avatars: owner writes, all authenticated users read
15 match /avatars/{userId}/avatar.{ext} {
16 allow read: if request.auth != null;
17 allow write: if request.auth != null
18 && request.auth.uid == userId
19 && request.resource.size < 5 * 1024 * 1024
20 && request.resource.contentType.matches('image/.*');
21 }
22
23 // Public assets: anyone reads, only admins write
24 match /public/{allPaths=**} {
25 allow read: if true;
26 allow write: if request.auth != null
27 && request.auth.token.admin == true;
28 }
29
30 // Shared project files: team members read/write
31 match /projects/{projectId}/{allPaths=**} {
32 allow read, write: if request.auth != null
33 && request.auth.token.projectIds is list
34 && projectId in request.auth.token.projectIds;
35 }
36
37 // Deny everything else
38 match /{allPaths=**} {
39 allow read, write: if false;
40 }
41 }
42}

Common mistakes when restricting Access to Firebase Storage Files

Why it's a problem: Using Firestore security rules syntax in Storage rules — the two are different DSLs

How to avoid: Storage rules use request.resource.size and request.resource.contentType for uploads, not resource.data like Firestore. Storage rules match file paths, not document paths.

Why it's a problem: Uploading to a path that does not match any security rule pattern, causing silent rejections

How to avoid: Ensure your client-side upload path exactly matches the pattern in your rules. If your rule expects /users/{userId}/avatar.jpg, uploading to /user-files/{uid}/avatar.jpg will be denied.

Why it's a problem: Forgetting that request.resource is only available on write operations, causing rules to fail on reads

How to avoid: Only use request.resource (size, contentType) in write rules. For read rules, use resource (without request.) to access the existing file's metadata.

Best practices

  • Always scope user files under a UID-based path like /users/{userId}/ to prevent cross-user access
  • Validate both file size and content type in write rules to prevent abuse and storage cost overruns
  • Use separate rule blocks for different access levels: private user files, shared team files, and public assets
  • Test rules in the Firebase Console Rules Playground before deploying to production
  • Set a maximum file size in rules as a server-side enforcement even if you validate client-side
  • Use custom claims (request.auth.token.admin) for role-based access rather than hardcoding UIDs in rules
  • Deploy rules with firebase deploy --only storage to update rules without affecting other services
  • Configure CORS on your Storage bucket to prevent unauthorized cross-origin access to download URLs

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Firebase Storage bucket where users upload profile pictures and documents. Write comprehensive Firebase Storage security rules that: restrict each user to their own /users/{uid}/ folder, limit avatar uploads to 5MB images only, limit document uploads to 10MB PDFs and images, make profile pictures readable by all authenticated users, and deny everything else.

Firebase Prompt

Generate Firebase Storage security rules for a multi-tenant application where users have private files under /users/{uid}/, team shared files under /teams/{teamId}/, and public assets under /public/. Include file size limits, content type validation, and role-based access using custom claims. Also show the client-side TypeScript upload code that matches these rule paths.

Frequently asked questions

Are Firebase Storage rules the same as Firestore rules?

No. They use a similar syntax but are separate systems with different objects. Storage rules use request.resource.size and request.resource.contentType for uploads, while Firestore rules use request.resource.data for document fields. They are defined in different files and deployed independently.

Can I restrict downloads to specific IP addresses?

No. Firebase Storage security rules cannot check IP addresses. For IP-based restrictions, use Cloud Storage IAM policies directly on the Google Cloud Console or put a Cloud Function proxy in front of Storage.

Do download URLs bypass security rules?

Yes. Download URLs generated by getDownloadURL() include a token that grants read access regardless of security rules. Anyone with the URL can access the file. To revoke access, regenerate the file's token in the Firebase Console or re-upload the file.

How do I make a file truly public?

Set allow read: if true in your Storage rules for the file's path. Alternatively, make the entire bucket public via Google Cloud Console IAM by granting allUsers the Storage Object Viewer role, but this bypasses all Firebase rules.

Can I use custom claims in Storage rules?

Yes. Access custom claims via request.auth.token in Storage rules. For example, request.auth.token.admin == true checks for an admin custom claim. Set custom claims using the Firebase Admin SDK server-side.

Can RapidDev help configure Firebase Storage security rules for my application?

Yes. RapidDev can design and implement Storage security rules tailored to your access patterns, including user-scoped files, team sharing, role-based access, and upload validation.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.