# How to Validate File Size in Firebase Storage

- Tool: Firebase
- Difficulty: Beginner
- Time required: 8-12 min
- Compatibility: Firebase JS SDK v9+, Firebase Storage, all Firebase plans
- Last updated: March 2026

## TL;DR

Validate file size in Firebase Storage at two layers: client-side by checking the File object's size property before calling uploadBytes, and server-side through Storage security rules using request.resource.size. The security rule is the critical layer because client-side checks can be bypassed. A rule like request.resource.size < 5 * 1024 * 1024 enforces a 5 MB limit regardless of how the upload is initiated.

## Enforcing File Size Limits in Firebase Storage

Accepting arbitrarily large file uploads can exhaust your storage budget and degrade user experience. This tutorial shows you how to validate file size both on the client side (for instant user feedback) and in Firebase Storage security rules (for enforcement that cannot be circumvented). You will also learn how to combine size limits with content type validation for a complete upload protection strategy.

## Before you start

- A Firebase project with Cloud Storage enabled
- Firebase JS SDK v9+ installed in your project
- Basic understanding of Firebase Storage security rules
- An HTML file input or file picker in your app

## Step-by-step guide

### 1. Add client-side file size validation

Before uploading, check the File object's size property (in bytes) against your maximum allowed size. This provides instant feedback to the user without consuming bandwidth or triggering a server-side rejection. Show a clear error message if the file is too large.

```
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5 MB in bytes

function validateFile(file: File): string | null {
  if (file.size > MAX_FILE_SIZE) {
    const sizeMB = (file.size / (1024 * 1024)).toFixed(1)
    return `File is ${sizeMB} MB. Maximum allowed size is 5 MB.`
  }
  return null // valid
}

// Usage with a file input
const input = document.querySelector('input[type="file"]') as HTMLInputElement
input.addEventListener('change', () => {
  const file = input.files?.[0]
  if (!file) return

  const error = validateFile(file)
  if (error) {
    alert(error)
    input.value = '' // clear the invalid selection
    return
  }

  // Proceed with upload
  uploadFile(file)
})
```

**Expected result:** Files larger than 5 MB are rejected immediately with a user-friendly error message before any upload begins.

### 2. Write Storage security rules to enforce size limits

Client-side validation can be bypassed by a malicious user. Storage security rules are enforced server-side and cannot be circumvented. Use request.resource.size to check the incoming file size. This rule rejects the upload at the Firebase level if the file exceeds the limit.

```
// storage.rules
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /uploads/{userId}/{allPaths=**} {
      allow read: if request.auth != null;
      allow write: if request.auth != null
                   && request.auth.uid == userId
                   && request.resource.size < 5 * 1024 * 1024;
    }
  }
}
```

**Expected result:** Any upload larger than 5 MB is rejected by Firebase Storage with a permission denied error, regardless of how the upload was initiated.

### 3. Combine size validation with content type checks

In addition to size limits, validate the content type to prevent users from uploading unexpected file types. Storage security rules can check request.resource.contentType. Combine both checks in a single rule for comprehensive upload protection.

```
// storage.rules
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /images/{userId}/{imageId} {
      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 /documents/{userId}/{docId} {
      allow read: if request.auth != null;
      allow write: if request.auth != null
                   && request.auth.uid == userId
                   && request.resource.size < 10 * 1024 * 1024
                   && request.resource.contentType in [
                     'application/pdf',
                     'application/msword',
                     'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
                   ];
    }
  }
}
```

**Expected result:** Images are limited to 5 MB and must be image/* content types. Documents are limited to 10 MB and must be PDF or Word files.

### 4. Upload with error handling for rule rejections

When a security rule rejects an upload, the Firebase SDK throws an error with the code 'storage/unauthorized'. Catch this error and display a helpful message. The user will see the client-side validation first, but the security rule serves as the backstop.

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

const storage = getStorage()

async function uploadFile(file: File) {
  // Client-side check first
  if (file.size > 5 * 1024 * 1024) {
    throw new Error('File exceeds 5 MB limit')
  }

  const storageRef = ref(storage, `uploads/${userId}/${file.name}`)

  try {
    const snapshot = await uploadBytes(storageRef, file, {
      contentType: file.type
    })
    console.log('Upload complete:', snapshot.metadata.fullPath)
    return snapshot
  } catch (error: any) {
    if (error.code === 'storage/unauthorized') {
      throw new Error('Upload rejected: file may exceed size limit or you lack permission')
    }
    throw error
  }
}
```

**Expected result:** Uploads that pass client-side validation but fail security rules are caught and surfaced to the user.

### 5. Add a progress indicator for valid uploads

For files within the size limit, use uploadBytesResumable to show upload progress. This improves user experience for larger files that take several seconds. The progress callback fires as bytes are transferred.

```
import { getStorage, ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage'

const storage = getStorage()

function uploadWithProgress(file: File, onProgress: (percent: number) => void) {
  const storageRef = ref(storage, `uploads/${userId}/${file.name}`)
  const uploadTask = uploadBytesResumable(storageRef, file, {
    contentType: file.type
  })

  return new Promise<string>((resolve, reject) => {
    uploadTask.on('state_changed',
      (snapshot) => {
        const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
        onProgress(Math.round(percent))
      },
      (error) => reject(error),
      async () => {
        const url = await getDownloadURL(uploadTask.snapshot.ref)
        resolve(url)
      }
    )
  })
}
```

**Expected result:** The progress callback fires with percentage updates during upload, and the function resolves with the download URL on completion.

## Complete code example

File: `src/lib/storage-upload.ts`

```typescript
// src/lib/storage-upload.ts
import {
  getStorage,
  ref,
  uploadBytesResumable,
  getDownloadURL
} from 'firebase/storage'

const storage = getStorage()

const SIZE_LIMITS: Record<string, number> = {
  image: 5 * 1024 * 1024,     // 5 MB
  document: 10 * 1024 * 1024,  // 10 MB
  video: 100 * 1024 * 1024     // 100 MB
}

function getFileCategory(file: File): string {
  if (file.type.startsWith('image/')) return 'image'
  if (file.type.startsWith('video/')) return 'video'
  return 'document'
}

export function validateFileSize(file: File): string | null {
  const category = getFileCategory(file)
  const maxSize = SIZE_LIMITS[category]
  if (file.size > maxSize) {
    const maxMB = (maxSize / (1024 * 1024)).toFixed(0)
    const fileMB = (file.size / (1024 * 1024)).toFixed(1)
    return `File is ${fileMB} MB. Maximum for ${category}s is ${maxMB} MB.`
  }
  return null
}

export function uploadFile(
  file: File,
  userId: string,
  onProgress?: (percent: number) => void
): Promise<string> {
  const sizeError = validateFileSize(file)
  if (sizeError) return Promise.reject(new Error(sizeError))

  const category = getFileCategory(file)
  const path = `${category}s/${userId}/${Date.now()}-${file.name}`
  const storageRef = ref(storage, path)
  const uploadTask = uploadBytesResumable(storageRef, file, {
    contentType: file.type
  })

  return new Promise((resolve, reject) => {
    uploadTask.on('state_changed',
      (snapshot) => {
        const percent = Math.round(
          (snapshot.bytesTransferred / snapshot.totalBytes) * 100
        )
        onProgress?.(percent)
      },
      (error) => {
        if (error.code === 'storage/unauthorized') {
          reject(new Error('Upload rejected by security rules'))
        } else {
          reject(error)
        }
      },
      async () => {
        const url = await getDownloadURL(uploadTask.snapshot.ref)
        resolve(url)
      }
    )
  })
}
```

## Common mistakes

- **Relying only on client-side validation without Storage security rules, allowing users to bypass size limits** — undefined Fix: Always enforce file size limits in Storage security rules using request.resource.size. Client-side checks improve UX but are not a security mechanism.
- **Using megabytes in security rules instead of bytes — writing request.resource.size < 5 expecting 5 MB but actually enforcing 5 bytes** — undefined Fix: Storage rule sizes are in bytes. Use 5 * 1024 * 1024 for 5 MB. Firestore rules support arithmetic expressions.
- **Not setting contentType in upload metadata, causing the contentType security rule check to fail** — undefined Fix: Always pass { contentType: file.type } in the upload metadata. Without it, Firebase may assign a default content type that does not match your rule.

## Best practices

- Validate file size on the client for instant user feedback, and in security rules for enforcement
- Use different size limits for different file types (images, documents, videos) by organizing uploads into separate storage paths
- Always set contentType in the upload metadata to ensure security rules can validate the file type
- Use uploadBytesResumable instead of uploadBytes for larger files so you can show progress and handle pauses
- Generate unique filenames using timestamps or UUIDs to prevent accidental overwrites
- For apps handling large volumes of user uploads with different validation requirements, RapidDev can help build a robust file processing pipeline with resizing and virus scanning

## Frequently asked questions

### What is the maximum file size Firebase Storage supports?

Firebase Storage supports files up to 5 TB. However, the practical limit for browser uploads is lower due to browser memory constraints and network timeouts. For files over 100 MB, use uploadBytesResumable which supports resumable uploads.

### Can I enforce different size limits for different file types in security rules?

Yes. Use separate match paths for different upload destinations and apply different size limits to each. For example, match /images/{path} with a 5 MB limit and match /videos/{path} with a 100 MB limit.

### Does the Spark (free) plan have different file size limits?

The Spark plan blocks executable file uploads (.exe, .dll, .apk) but does not impose a different maximum file size compared to Blaze. The 5 GB total storage limit on Spark is the main constraint.

### How do I check file size in a React component?

Access event.target.files[0].size in the onChange handler of your file input. The size is in bytes. Compare it to your maximum before calling the upload function.

### What error does Firebase throw when a file exceeds the security rule size limit?

Firebase throws an error with code 'storage/unauthorized' and message 'User does not have permission to access'. The error does not specifically mention the size limit — it is a generic permission denied. Your client-side validation should provide the specific size error message.

### Can I validate file dimensions (width and height) in security rules?

No. Storage security rules cannot inspect image dimensions. You can check dimensions client-side using the Image API (create an Image element and check naturalWidth/naturalHeight), or validate server-side using a Cloud Function triggered by Storage uploads.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-validate-file-size-in-firebase-storage
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-validate-file-size-in-firebase-storage
