Get in Touch
S3 vs Supabase Storage vs Firebase Storage: Which Should You Use? - Red Surge Technology Blog
SupabaseFirebaseAWS S3StorageBackend

Object storage is one of those decisions that feels minor until it isn't. You add a file upload button, pick the storage service that's already in your stack, and move on. Six months later you're looking at an egress bill that costs more than your database, or you're trying to implement per-user file permissions and realizing the storage layer has no idea who your users are.

AWS S3, Supabase Storage, and Firebase Storage sit at three different points on the spectrum between "infrastructure primitive" and "integrated application service." S3 is the raw storage layer that powers a huge portion of the internet. Supabase and Firebase are opinionated platforms that bundle storage with authentication, databases, and client SDKs.

This guide compares them on the dimensions that actually drive the decision: pricing, security models, upload behavior, and portability. If you're already comparing Supabase and Firebase for the database, our Supabase vs Firebase RLS and real-time comparison covers that side. This post is specifically about file storage. And if you're optimizing asset delivery, our Cache-Control HTTP header guide covers the caching layer that sits in front of all three.

The Core Architectural Difference

S3 is a storage primitive. It stores objects in buckets and exposes an HTTP API. It doesn't know about your users. It doesn't have a concept of a "logged-in session." Authorization is handled by IAM policies, bucket policies, and presigned URLs. You build the application layer that decides who can upload and download what.

Supabase Storage is an S3-compatible storage layer built into the Supabase platform. It stores objects in buckets, but it uses Postgres Row-Level Security to control access. Because Supabase also owns authentication and the database, you can write storage policies that reference your application's user data directly. It's an integrated service, not a raw primitive.

Firebase Storage is Google Cloud Storage wrapped with Firebase SDKs and Security Rules. It's designed for client-side uploads from mobile and web apps, with built-in resumable uploads and progress monitoring. Access control is handled by Firebase Security Rules, which evaluate against the authenticated Firebase user.

The practical consequence: S3 gives you maximum control and portability at the cost of integration work. Supabase and Firebase give you a faster path to working uploads at the cost of coupling to their platforms.

Pricing: The Real Cost of Storing and Serving Files

Storage pricing is where the differences are most visible, and where the marketing pages are most misleading. The per-gigabyte storage cost is only part of the story. Egress—the cost of serving files to users—is often the dominant line item.

Here's the comparison based on published rates for standard storage tiers.

Provider Storage ($/GB/month) 1TB stored Egress ($/GB)
AWS S3 $0.023 $23.00 $0.09
Supabase Storage $0.0213 $25.00 $0.09 (uncached) / $0.03 (cached)
Firebase Storage $0.026 $100.00* $0.12

*Firebase's 1TB estimate includes egress and operation costs under typical usage, not storage alone. Storage itself is around $0.026/GB/month.

A few things stand out.

S3 is the cheapest on raw storage but the most expensive on egress if you serve files directly from S3. AWS's own guidance is explicit: serving 1TB per month directly from S3 costs about $92, while serving it through CloudFront costs about $8.50. The fix is to put a CDN in front of S3, which is standard practice but adds configuration and cost.

Supabase Storage is priced almost identically to S3 for storage and uncached egress, but it includes a CDN and charges less for cached egress. Its Smart CDN synchronizes asset metadata to the edge and automatically revalidates when files change. If your files are served through the CDN—which they should be—the effective egress cost is lower than S3's default.

Firebase Storage is the most expensive on egress, at $0.12/GB. It also now requires the Blaze pay-as-you-go plan for Cloud Storage, even at minimal volumes, since February 2026. The free tier still applies on Blaze, but you need a billing account. For read-heavy applications, this is the most expensive of the three.

The cost picture changes entirely at scale. If you're serving terabytes of media per month, Cloudflare R2's free egress makes it worth considering alongside these three. But within this comparison, S3 plus CloudFront is the cheapest at high volume, Supabase is competitive and simpler, and Firebase is the most expensive for egress-heavy workloads.

Security: RLS vs Security Rules vs IAM

Storage security is where the three platforms diverge most sharply. Each has a different model for answering the question: can this user access this file?

Supabase Storage: Postgres RLS on storage.objects

Supabase Storage uses the same Row-Level Security system as the rest of the platform. Policies are SQL expressions attached to the storage.objects table. By default, Storage doesn't allow any uploads without RLS policies—you have to explicitly grant access.

-- Allow authenticated users to upload to their own folder
create policy "Users can upload to own folder"
on storage.objects
for insert
to authenticated
with check (
  bucket_id = 'documents'
  and (storage.foldername(name))[1] = (select auth.jwt()->>'sub')
);
-- Allow users to read their own files
create policy "Users can read own files"
on storage.objects
for select
to authenticated
using (
  bucket_id = 'documents'
  and owner_id = (select auth.jwt()->>'sub')
);

The power here is that storage policies are SQL. You can reference other tables, check roles, validate file types, and enforce size limits—all in the same language you use for the rest of your data. The helper functions like storage.foldername(name) make path-based policies straightforward.

The risk is the same as RLS on any table: a missing policy or a misconfigured with check can expose files. The service key bypasses RLS entirely, so it must never be exposed to clients. Our Supabase vs Firebase RLS comparison covers the failure modes in more depth.

Firebase Storage: Security Rules

Firebase Storage uses Security Rules, a declarative DSL evaluated before every read and write. Rules have access to request.auth, which contains the authenticated Firebase user.

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

Firebase Rules are simpler for straightforward user-scoped paths, but less expressive than SQL for complex authorization logic. Cross-document checks in Storage Rules are limited—rules evaluate against the request and the object metadata, not arbitrary database queries.

The bigger structural difference is that Firebase Rules are a separate configuration artifact. They live in a different syntax, they're deployed separately, and they don't share a language with your application's data model. That's fine for simple cases; it creates friction when authorization logic spans storage and database.

AWS S3: IAM and Presigned URLs

S3 has the most flexible and the most complex security model. Authorization is handled by IAM policies (attached to users and roles) and bucket policies (attached to buckets). S3 doesn't know about your application's users, so the standard pattern is to generate a presigned URL on your server after checking permissions in your own database.

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3 = new S3Client({ region: "us-east-1" });

async function getDownloadUrl(key, userId) {
  // Check your database first
  const file = await db.files.findUnique({ where: { key } });
  if (file.ownerId !== userId) {
    throw new Error("Forbidden");
  }

  const command = new GetObjectCommand({
    Bucket: "my-bucket",
    Key: key,
  });

  return getSignedUrl(s3, command, { expiresIn: 3600 }); // 1 hour
}

Presigned URLs expire. The console allows 1 minute to 12 hours; the SDK allows up to 7 days. They're a delivery mechanism, not an access-control policy—the database remains the place where you decide who can download what. The risk is that a leaked presigned URL grants access until it expires, so short expiration times are essential.

S3's model is the most powerful because it's the most general. It's also the most work, because you're building the authorization layer yourself.

Real-Time Upload Feedback and Resumable Uploads

If you're building a mobile app, upload feedback matters. Users on unreliable networks need to see progress, pause, and resume. The three platforms handle this differently.

Firebase Storage has the strongest client-side upload story. The uploadBytesResumable() method provides progress events, pause/resume, and cancel. Resumable uploads retry transparently on mobile, and the SDK surfaces state changes through a task snapshot. For a mobile app where upload experience is part of the product, this is the most mature implementation.

const task = uploadBytesResumable(storageRef, file);

task.on(
  "state_changed",
  (snapshot) => {
    const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
    console.log(`Upload is ${progress}% done`);
  },
  (error) => {
    console.error(error);
  },
  () => {
    console.log("Upload complete");
  },
);

Supabase Storage supports resumable uploads through its S3-compatible protocol, and the JavaScript SDK handles standard uploads. The real-time feedback story isn't as polished as Firebase's—there's no built-in progress event stream in the same way—but for most web applications, standard upload with a loading state is sufficient.

S3 supports multipart uploads and resumable uploads via the SDK, but you implement the progress tracking and resume logic yourself. The AWS SDKs provide the primitives, but the client-side experience is your responsibility.

The practical takeaway: if rich, real-time upload monitoring on mobile is a core product requirement, Firebase Storage has the advantage. If your uploads are standard web uploads—a profile photo, a document, a CSV—all three are capable, and the integration cost favors Supabase.

Image Transformations and CDN Behavior

Serving images at the right size is one of the highest-leverage performance optimizations you can make. The three platforms differ here too.

Supabase Storage includes on-the-fly image transformations on Pro plans and above. You append width, height, resize, and quality parameters to the URL, and Supabase returns a transformed image. It also automatically serves WebP to browsers that support it, which reduces egress and speeds up delivery. The free tier serves original files without transformation.

const { data } = supabase.storage.from("images").getPublicUrl("photo.jpg", {
  transform: { width: 500, height: 600 },
});

Firebase Storage does not include built-in image transformations. You use the Firebase Resize Image extension, which uses Cloud Functions to generate resized versions on upload. It works, but it's an add-on with additional cost and complexity.

S3 has no built-in image transformations. You either pre-generate resized versions, use a service like Cloudinary or Imgix, or run transformations through Lambda@Edge. The flexibility is high; the integration work is significant.

Supabase's built-in transformations are a meaningful advantage for applications that serve user-uploaded images. It's one less service to integrate, and the WebP optimization directly reduces bandwidth costs.

Portability and Lock-In

This is where S3's position as a primitive becomes a strength.

S3 is the de facto standard for object storage. Its API is so widely adopted that almost every alternative is S3-compatible: Cloudflare R2, Backblaze B2, DigitalOcean Spaces, Wasabi, Supabase Storage itself. If you build on S3, you can migrate to a cheaper S3-compatible provider without rewriting your upload logic. You can use the same SDK, the same CLI, and the same mental model.

Supabase Storage is S3-compatible. That means you can migrate away from Supabase to any S3 provider later without changing your client code. The RLS policies are Postgres-specific, so authorization logic doesn't port cleanly, but the object storage layer does. This is a meaningful form of portability that Firebase doesn't offer.

Firebase Storage is Google Cloud Storage under the hood, but the client SDKs and Security Rules are Firebase-specific. Migrating off Firebase means rewriting upload logic, authorization rules, and download URL generation. It's not impossible, but it's a larger effort than moving between S3-compatible providers.

If long-term flexibility matters—and for infrastructure decisions, it usually should—S3 and Supabase Storage are the more portable choices.

A Decision Framework

Here's how I choose between the three.

Choose AWS S3 when:

  • You need the broadest ecosystem and tooling support.
  • You're already on AWS and using IAM, CloudFront, and the rest of the stack.
  • You need to store very large files (multi-gigabyte) and want the mature multipart upload tooling.
  • Long-term portability between S3-compatible providers is a priority.
  • You're comfortable building the authorization layer yourself.

Choose Supabase Storage when:

  • Your application already uses Supabase for database and auth.
  • You want storage permissions to live alongside your relational data in Postgres RLS.
  • You want built-in image transformations and a CDN without additional integration.
  • You want S3 compatibility without managing S3 infrastructure.
  • Your uploads are standard web uploads, not mobile-first with rich progress UI.

Choose Firebase Storage when:

  • Your app is mobile-first and needs resumable uploads with progress monitoring.
  • You're already using Firebase Authentication and Security Rules.
  • You want the most integrated client-side upload experience.
  • Your files are document-shaped and don't require relational authorization.
  • Egress cost is not the dominant concern.

If you're building a server-side marketplace or SaaS application with relational ownership data, Supabase Storage is usually the simplest fit. If you're building a mobile app where upload experience is central, Firebase Storage has the edge. If you're building infrastructure that needs to outlive the current application framework, S3 is the baseline.

A Real Story: The Egress Bill That Surprised Everyone

A team I worked with built a media-sharing feature on S3. They stored user-uploaded images and served them directly from the bucket. The storage cost was negligible—a few dollars a month. Then the feature went viral, and their egress bill went from $20 to $1,400 in a single month.

The fix was straightforward: put CloudFront in front of the bucket, set long cache TTLs, and serve transformed images at the right sizes. The egress cost dropped by roughly 90%. But it took a billing surprise to make the team prioritize the CDN layer.

The lesson: with S3, storage is cheap but delivery is not. You have to design the delivery path deliberately. Supabase Storage includes the CDN and the transformations by default, which is why it's often cheaper in practice despite similar headline storage rates. Firebase's egress pricing makes the delivery path expensive by default.

If you've been optimizing asset delivery, our Cache-Control HTTP header guide covers the caching directives that make the biggest difference, and our Redis cache design patterns guide covers server-side caching for the metadata around your files.

Wrapping Up

S3, Supabase Storage, and Firebase Storage aren't interchangeable. S3 is a primitive that gives you control and portability at the cost of integration work. Supabase Storage is an integrated service that ties storage permissions to your relational data. Firebase Storage is a mobile-first storage layer with the best client-side upload experience.

The pricing is similar on paper, but delivery costs diverge sharply. S3 needs a CDN to be cost-effective at scale. Supabase includes one. Firebase charges the most for egress. Security models follow the same pattern: S3 is the most flexible and the most work, Supabase is the most relational, Firebase is the simplest for user-scoped paths.

For most teams building a SaaS or marketplace on Supabase, Supabase Storage is the natural choice. For mobile-first apps on Firebase, Firebase Storage is the path of least resistance. For everything else—especially infrastructure that needs to last—S3 plus a CDN is the durable baseline.

For the broader platform comparison, see our Supabase vs Firebase RLS and real-time guide. For caching strategies that reduce egress across all three, our Redis cache design patterns and Cache-Control HTTP header guide have you covered.

Now go put a CDN in front of that bucket before the bill arrives.


Need help choosing or architecting your file storage layer? Red Surge Technology builds secure, cost-efficient storage pipelines for modern applications. Get in touch to discuss your project.