# How to Restrict Access to Firebase Storage Files

- Tool: Firebase
- Difficulty: Intermediate
- Time required: 15-20 min
- Compatibility: Firebase Storage (Spark and Blaze plans), Firebase JS SDK v9+
- Last updated: March 2026

## 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.

## Before you start

- 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.

```
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    // Default: deny all access
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}
```

**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.

```
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    // Only authenticated users can read or write
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}
```

**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.

```
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    // Each user can only access their own folder
    match /users/{userId}/{allPaths=**} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }

    // Public assets readable by anyone, writable by admins
    match /public/{allPaths=**} {
      allow read: if true;
      allow write: if request.auth != null
                   && request.auth.token.admin == true;
    }
  }
}
```

**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.

```
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/avatar.{ext} {
      // Only the file owner can upload their avatar
      // Max 5MB, images only
      allow read: if request.auth != null;
      allow write: if request.auth != null
                   && request.auth.uid == userId
                   && request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }

    match /users/{userId}/documents/{document} {
      // Max 10MB, PDFs and images only
      allow read: if request.auth != null
                  && request.auth.uid == userId;
      allow write: if request.auth != null
                   && request.auth.uid == userId
                   && request.resource.size < 10 * 1024 * 1024
                   && (request.resource.contentType == 'application/pdf'
                       || request.resource.contentType.matches('image/.*'));
    }
  }
}
```

**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.

```
# Deploy only storage rules
firebase deploy --only storage

# Or deploy everything
firebase 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.

```
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage'
import { getAuth } from 'firebase/auth'

const storage = getStorage()
const auth = getAuth()

async function uploadAvatar(file: File) {
  const user = auth.currentUser
  if (!user) throw new Error('Must be signed in to upload.')

  // Path must match the security rule pattern
  const avatarRef = ref(storage, `users/${user.uid}/avatar.jpg`)

  try {
    const snapshot = await uploadBytes(avatarRef, file, {
      contentType: file.type
    })
    const downloadUrl = await getDownloadURL(snapshot.ref)
    console.log('Avatar uploaded:', downloadUrl)
    return downloadUrl
  } catch (error: any) {
    if (error.code === 'storage/unauthorized') {
      console.error('Upload rejected by security rules.')
    } else {
      console.error('Upload failed:', error.message)
    }
    throw error
  }
}
```

**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 code example

File: `storage.rules`

```text
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {

    // User-scoped files: only the owner can read/write
    match /users/{userId}/{allPaths=**} {
      allow read: if request.auth != null
                  && request.auth.uid == userId;
      allow write: if request.auth != null
                   && request.auth.uid == userId
                   && request.resource.size < 10 * 1024 * 1024;
    }

    // User avatars: owner writes, all authenticated users read
    match /avatars/{userId}/avatar.{ext} {
      allow read: if request.auth != null;
      allow write: if request.auth != null
                   && request.auth.uid == userId
                   && request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }

    // Public assets: anyone reads, only admins write
    match /public/{allPaths=**} {
      allow read: if true;
      allow write: if request.auth != null
                   && request.auth.token.admin == true;
    }

    // Shared project files: team members read/write
    match /projects/{projectId}/{allPaths=**} {
      allow read, write: if request.auth != null
                         && request.auth.token.projectIds is list
                         && projectId in request.auth.token.projectIds;
    }

    // Deny everything else
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}
```

## Common mistakes

- **Using Firestore security rules syntax in Storage rules — the two are different DSLs** — undefined Fix: 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.
- **Uploading to a path that does not match any security rule pattern, causing silent rejections** — undefined Fix: 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.
- **Forgetting that request.resource is only available on write operations, causing rules to fail on reads** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-restrict-access-to-firebase-storage-files
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-restrict-access-to-firebase-storage-files
