Firebase Storage — Image Upload and Security: A Hands-On Guide for Developers
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

Firebase Storage — Image Upload and Security: A Hands-On Guide for Developers

[2026-07-16] Author: Ing. Calogero Bono
> share
Zenithby Meteora Web The operating system for your business. Social, clients, bookings and invoices in one platform. Gyms, barbers, professionals. Discover Zenith Free demo · no card

You’ve integrated Firebase Storage. Image upload works. Then you discover someone uploaded a 2 GB file to your public bucket. Or that your security rules allow anyone to read every user photo. It happens more often than you think.

Here at Meteora Web, we’ve seen projects where Firebase Storage rules were still the default — "allow all" — because "the frontend is secure enough". The frontend is never secure enough. A public bucket without controls is a ticking bill. In this hands-on guide we’ll configure secure image upload and Firebase Storage rules, real code included.

Why is Firebase Storage security a real problem?

Firebase Storage runs on Google Cloud Storage. Every file gets a unique URL. If rules are misconfigured, anyone with that URL can download it. Worse: without upload limits, a malicious user can fill your bucket and spike your costs.

Common mistakes we see:

  • Open read/write rules for anonymous users
  • No file type or size validation at the rule level
  • Uploading directly from the frontend without an auth token
  • Files never cleaned after user account deletion

These aren’t abstract scenarios. An e-commerce client had product images with public read rules. A competitor scraped the entire image catalog in 10 minutes – zero cost for them, thousands in bandwidth for the client. We always think in terms of cost and risk: a wrong security rule is a hidden cost.

Sponsored Protocol

How to configure security rules for Firebase Storage?

Rules are written in the Firebase console, under Storage -> Rules, in a declarative syntax. Here are the three essential patterns.

1. Public read, authenticated write

For profile pictures, public previews. Anyone can read, only logged-in users can upload.

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read: if true;
      allow write: if request.auth != null;
    }
  }
}

2. User-scoped folders

Each user can only read/write their own folder. Perfect for private profiles or documents.

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

3. File type and size validation

Prevent executable uploads or huge files by checking request.resource.

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      allow write: if request.auth != null
        && request.resource.size < 5 * 1024 * 1024   // 5 MB
        && request.resource.contentType.matches('image/.*');
      allow read: if true;
    }
  }
}

Important: request.resource only exists during write operations. This server-side validation is your first defense — it works even if the client is compromised.

Sponsored Protocol

How to implement image upload from web or mobile?

We use the Firebase v9 modular SDK. Below is an upload snippet with progress feedback and custom metadata.

Upload with metadata and progress

import { getStorage, ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage';
import { getAuth } from 'firebase/auth';

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

function uploadImage(file) {
  const user = auth.currentUser;
  if (!user) throw new Error('User not authenticated');

  const storageRef = ref(storage, `users/${user.uid}/images/${Date.now()}_${file.name}`);

  const metadata = {
    contentType: file.type,
    customMetadata: {
      'uploadedBy': user.uid,
      'originalName': file.name
    }
  };

  const uploadTask = uploadBytesResumable(storageRef, file, metadata);

  uploadTask.on('state_changed', 
    (snapshot) => {
      const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
      console.log('Upload progress:', progress);
    },
    (error) => {
      console.error('Upload error:', error);
    },
    () => {
      getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
        console.log('File available at:', downloadURL);
        // Store URL in Firestore or elsewhere
      });
    }
  );
}

Why this approach? The path includes the user UID, matching the rule from section 2. Custom metadata enables audit trails. Progress avoids poor UX. Error handling catches auth failures (403).

Sponsored Protocol

Mobile upload (Flutter example)

import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:io';

Future uploadImage(File file) async {
  final user = FirebaseAuth.instance.currentUser;
  if (user == null) throw Exception('Not authenticated');
  final ref = FirebaseStorage.instance
      .ref('users/${user.uid}/images/${DateTime.now().millisecondsSinceEpoch}_${file.path.split('/').last}');
  final snapshot = await ref.putFile(file);
  return await snapshot.ref.getDownloadURL();
}

What steps to take for storage and cost optimization?

Upload is just the beginning. Without size and quality controls, your bucket grows and costs rise. Here are our best practices.

Sponsored Protocol

Client-side compression and resizing

Before uploading, reduce the image on the device. On web, use canvas to resize. On mobile, use libraries like image_picker with maxWidth option. We cut image weight by 60% for one client with zero visual loss, simply by resizing to 1200px on the longest side.

Generate thumbnails with Cloud Functions

Trigger a Cloud Function on object finalization to create a smaller version. Example with Sharp on Node.js:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const sharp = require('sharp');

admin.initializeApp();

exports.generateThumbnail = functions.storage.object().onFinalize(async (object) => {
  const bucket = admin.storage().bucket(object.bucket);
  const filePath = object.name;
  const fileName = filePath.split('/').pop();
  const thumbPrefix = 'thumb_';
  if (fileName.startsWith(thumbPrefix)) return null;

  const thumbPath = filePath.replace(fileName, `${thumbPrefix}${fileName}`);
  const downloadedFile = bucket.file(filePath);
  const tempLocalPath = `/tmp/${fileName}`;
  const thumbLocalPath = `/tmp/${thumbPrefix}${fileName}`;

  await downloadedFile.download({destination: tempLocalPath});
  await sharp(tempLocalPath).resize(200, 200).toFile(thumbLocalPath);
  await bucket.upload(thumbLocalPath, {destination: thumbPath});
});

Retention policies and automatic cleanup

Set GCS Lifecycle Management to delete files not read after 90 days. Or, when a user is deleted, trigger a Cloud Function to remove their Storage folder. We always do this — one client forgot to clean up and storage costs silently accumulated.

Sponsored Protocol

What to do now

Three concrete actions for your Firebase project today:

  1. Check your current rules in Firebase console > Storage > Rules. If you have allow read, write: if true; in production — fix it immediately. Use at least the authenticated-user pattern.
  2. Add type and size validation in the rules. Never trust the frontend alone. Quick test: try uploading a .exe file — if it passes, your rules are broken.
  3. Implement upload with UID-based paths and progress. If you already use the SDK, verify that the path exactly matches the match /users/{userId}/... rule.

For a complete overview of the Firebase ecosystem, read our pillar guide on Firebase for web and mobile apps (Italian).

> share
Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()