Picking free object storage in 2026 is harder than the free-GB line suggests
I compared six object storage options that still show up in every “what do I use for file uploads” thread: Cloudflare R2, Backblaze B2, Supabase Storage, Firebase Storage, Storj, and UploadThing. The Reddit-style answers mostly ignore that these products do not meter the same unit, and that the scary bill in a viral month is often egress, not storage.
For an indie app where the nightmare is “my side project hits the front page and my bandwidth bill explodes,” my default pick is Cloudflare R2. Public pricing says internet egress is free for every storage class. That is unusually blunt compared to the rest of this list. The tradeoff is that R2 is S3-compatible but not full-parity S3, and at very high read counts the bill shifts from transfer fees to Class B request charges.
Backblaze B2 is the runner-up I would actually ship if I wanted the most conventional S3 story: 10 GB free storage, free Class A/B/C transactions, and free egress up to 3x your average monthly stored data. After that, outbound is $0.01/GB, and several partner CDNs (including Cloudflare) can make B2-side egress effectively free. The catch is the ratio. Store 10 GB, serve 2 TB, and the 3x rule will not save you.
If you want upload UX more than bucket portability, UploadThing is the ergonomic fork: 2 GB free, unlimited uploads and downloads on the pricing page, first-party Next.js adapters. If you want auth and storage rules in one product, Supabase Storage or Firebase Storage are the platform plays. Neither is what I would pick purely for viral outbound traffic predictability.
You cannot compare these on storage GB alone
Vendors bill storage, egress, API requests, and platform bundles differently. Pick the wrong mental model and you will mis-budget the month your app gets shared everywhere.
| Mental model | Providers | What burns quota |
|---|---|---|
| Storage plus free egress | R2, B2 (with caveats), UploadThing | GB stored; B2 also cares about download-to-storage ratio |
| Platform bundle | Supabase, Firebase | Storage plus bandwidth plus auth integration |
| Per-TB egress | Storj | Egress terabytes; $50/mo minimum from July 1, 2026 |
S3 compatibility also splits three ways in this set. Full means a native S3-style endpoint with mainstream AWS SDK tooling in normal use. Partial means S3 access exists but parity is incomplete. Adapter required means the normal developer path is provider-native, not S3-first.
You will not get a perfect apples-to-apples table. Use the matrix below as directional, then read the egress section if your app could suddenly serve a lot of downloads.
Free tiers and S3 fit at a glance
| Provider | Free tier | Egress beyond allowance | S3 compatibility | Auth and SDKs | Regions | Hard limits | Bottom line |
|---|---|---|---|---|---|---|---|
| Cloudflare R2 | 10 GB-month storage, 1M Class A ops, 10M Class B ops, egress free each month | Internet egress free. Paid: $0.015/GB-month storage, Class A $4.50/M, Class B $0.36/M | Partial. Some S3 features unimplemented | R2 API tokens as S3 creds; AWS SDK examples; Workers bindings | Auto placement; optional location hints (NA, EU, APAC, Oceania) | 5 TiB object size; 1 write/sec per key; r2.dev not for production | Best if egress-fee shock is the fear. Request-heavy tiny-object workloads can still cost real money. |
| Supabase Storage | Free: 1 GB storage; org egress 5 GB uncached + 5 GB cached. Paid includes 100 GB storage and 250 GB + 250 GB egress before overage | Paid overage: $0.09/GB uncached, $0.03/GB cached | Partial. S3 protocol supported; versioning not supported | supabase-js, Auth + RLS, S3 keys (server keys bypass RLS) | One primary AWS region per project | Free projects: 50 MB global file limit; standard uploads to 5 GB; paid up to 500 GB global limit | Best when storage should follow auth and RLS. Weak for viral bandwidth predictability. |
| Firebase Storage | Current *.firebasestorage.app buckets in us-central1/us-west1/us-east1: 5 GB-months storage, 100 GB/month downloaded, 5K uploads, 50K downloads. Requires Blaze plan | Falls through to GCS pricing: ~$0.12/GiB general internet egress (first 10 TiB) | Adapter required. Normal path is Firebase/GCS-native | Web/mobile SDKs, Security Rules, Admin SDK, GCS APIs underneath | GCS bucket locations; free allowances tied to eligible US regions above | 5 TiB object size; 1 write/sec per object; egress quotas can return 429 | Good inside Firebase. Weak for S3 portability or card-free “free storage.” |
| Backblaze B2 | First 10 GB storage free. Free egress up to 3x average monthly stored data. Class A/B/C free; Class D $0.004/10K after 2,500/day free | Beyond 3x stored: $0.01/GB. Partner CDN/compute routes can zero out B2 egress | Full. Native S3 API and AWS SDK guides | S3-style app keys; native B2 API optional | US East, US West, EU Central, Canada East per account (fixed at signup) | ~500 req/sec default; 100 buckets/account; S3 API returns 503 on throttle | Strong value when downloads are not wildly larger than stored data, or behind a partner CDN. |
| Storj | No durable free tier; 30-day trial (Object Mount materials mention 25 GB for 30 days) | $7/TB egress, no API fees. $50 minimum monthly fee effective July 1, 2026 | Partial. S3 gateway with documented gaps | Access Grants, S3 creds from console, Uplink tooling | Standard: globally distributed; Advanced: US SOC 2 Type 2 DCs | Paid: 25 TB storage, 100 TB egress, 100 req/sec, 100 buckets/project per project | Interesting per-TB economics if you want distributed storage. Weak for “free tier first.” |
| UploadThing | 2 GB storage, 7-day audit log retention, unlimited uploads/downloads on pricing page | No separate egress meter published; billing is storage-plan-centric | Adapter required. Not a raw S3 endpoint | UPLOADTHING_TOKEN, framework adapters, file-route middleware, UTApi | Default AWS us-west-2; paid plans add region selection | Per-route file type, size, count limits; callback endpoint must stay public; signed URLs max 7 days | Fastest Next.js upload ship. Weakest for raw bucket control or S3 portability. |
Among these six, only R2 really behaves like a viral-safe object store on bandwidth by default. B2 can get close behind a partner CDN. UploadThing looks equally gentle on public pricing, but it is a managed upload layer, not a general-purpose bucket.
If decentralized positioning itself is the requirement, Filebase advertises 5 GB pooled free storage with free object egress, and Akave markets zero-egress S3-compatible storage without a similarly meaningful free tier in the materials I read. I did not fully review either here.
Egress economics when traffic spikes
The table below is egress only. It ignores included free quotas unless noted, excludes storage and request charges, and uses round 100 GB / 1 TB / 10 TB months for readability. Firebase’s underlying GCS pricing is quoted in GiB/TiB, so real invoices will differ slightly.
| Scenario | 100 GB/mo | 1 TB/mo | 10 TB/mo | Notes |
|---|---|---|---|---|
| Cloudflare R2 | $0 | $0 | $0 | Public internet egress free. Storage and requests billed separately. |
| Supabase uncached overage | $9 | $90 | $900 | Paid-plan $0.09/GB uncached; ignores included quota |
| Supabase cached overage | $3 | $30 | $300 | Paid-plan $0.03/GB cached; ignores included quota |
| Firebase raw GCS overage | $12 | $120 | $1,200 | GCS ~$0.12/GiB general internet; simplified as $0.12/GB |
| Firebase with eligible-bucket allowance | $0 | $108 | $1,188 | Subtracts 100 GB/month free download for current bucket family |
| Backblaze B2 at 100 GB stored | $0 | $7 | $97 | 3x rule: 300 GB free egress, then $0.01/GB |
| Backblaze B2 at 1 TB stored | $0 | $0 | $70 | 3 TB free egress with 1 TB stored |
| Storj egress only | $0.70 | $7 | $70 | $7/TB egress; excludes storage and $50 minimum |
| UploadThing (public pricing) | $0 | $0 | $0 | No separate bandwidth meter on pricing page; storage plan still applies |
xychart-beta
title "Representative viral-spike egress bill for a 10 TB month"
x-axis ["R2","Supabase cached","Supabase uncached","Firebase adjusted","B2 at 100 GB stored","Storj","UploadThing"]
y-axis "USD" 0 --> 1200
bar [0,300,900,1188,97,70,0] The chart is not saying Storj always beats Backblaze or UploadThing is “better storage” than R2. The shape of viral risk differs sharply. R2 and UploadThing mostly remove the bandwidth line item. Backblaze makes egress small but ratio-sensitive. Storj keeps per-TB egress low but adds a billing floor. Supabase and Firebase can hurt if your app turns into a download hose.
Two subtleties worth separating.
R2 changes the failure mode from egress to requests. Cloudflare’s own pricing example for hosting 100,000 small files with 10 million object reads per day lands around $104.40/month in Class B charges while egress stays free. R2 is excellent for “my CDN transfer bill exploded,” not literally free for high-read, tiny-asset workloads.
Backblaze’s free egress is storage-coupled unless you use a listed partner CDN or compute route. Store little, stream a lot, and the 3x number is what matters. Already on Cloudflare, Fastly, or bunny.net? The B2-side egress risk drops a lot.
Firebase has its own fork: front GCS with Cloud CDN and Google says storage transfer charges can be waived, but that is no longer “just Firebase Storage pricing.” You are now running a multi-product CDN architecture with cache-fill economics. Valid, but less simple than R2’s native no-egress-fee position.
What the free tiers actually cost you later
Cloudflare R2: best viral-bandwidth predictability, watch request pricing
R2’s free tier is generous enough for side projects: 10 GB-month, a million Class A operations, ten million Class B operations, and egress that does not meter on the public page. Paid storage at $0.015/GB-month is reasonable.
The S3 story is real but incomplete. Cloudflare’s compatibility page lists unimplemented operations. For typical app uploads and signed delivery, that is usually fine. For exotic S3 tooling, test before you commit.
My main warning is request economics, not egress. Asset hosts with millions of small reads can still see a meaningful bill. Budget both dimensions.
Backblaze B2: best mainstream S3 parity, ratio-sensitive egress
B2 is what I recommend when someone says “I want S3, but cheaper, and I will use the AWS SDK.” Ten gigabytes free forever, free Class A/B/C transactions, and the 3x egress rule are unusually friendly for small teams.
The storage-to-download ratio is the whole game. Viral file delivery from a small bucket without a partner CDN is exactly the scenario where B2’s free egress stops feeling free. Put Cloudflare or another partner in front and the picture changes.
Account region is chosen at signup (US East, US West, EU Central, Canada East) and cannot be moved later. Plan that once.
Supabase Storage: best with Supabase Auth and RLS, expensive uncached egress
Supabase Storage shines when you already run Supabase Auth, Postgres, and RLS. Browser uploads through supabase-js, JWT-backed session S3 access that respects policies, and server S3 keys when you need them (knowing server keys bypass RLS).
Free tier is tight: 1 GB storage and 5 GB + 5 GB org egress. Paid overage at $0.09/GB uncached and $0.03/GB cached means CDN hit ratio matters a lot. A viral month on uncached delivery gets expensive fast.
S3 access exists but is partial. No versioning. Fine for app storage, not a drop-in replacement for every S3 workflow.
Firebase Storage: good Firebase ergonomics, Blaze required, GCS underneath
Firebase Storage is the obvious pick when you already live in Firebase Auth, Security Rules, and the mobile/web SDKs. Direct client uploads with rules tied to identity is a nice developer experience.
Current docs require the Blaze plan even though usage allowances still exist. That is not a clean no-card free storage story anymore.
Free allowances for the current *.firebasestorage.app bucket family are tied to us-central1, us-west1, and us-east1. Beyond that, you are in GCS pricing land. Treat Firebase as a platform choice, not a generic bucket.
Storj: interesting per-TB model, trial-only free tier, billing floor coming
Storj charges $7/TB for storage and $7/TB for egress with no API fees. The decentralized architecture is the point, not a footnote.
For indie “free tier first” evaluation, the picture is weaker. Durable free object storage is basically gone (30-day trial). A $50 minimum monthly fee starts July 1, 2026. I would only pick Storj here if distributed storage is a product requirement, not because it won a free-GB comparison.
UploadThing: fastest Next.js ship, not a portable S3 bucket
UploadThing is not raw object storage. It is a managed upload and delivery product with first-party Next.js adapters, file-route middleware for auth, and callbacks that behave like webhooks.
Two gigabytes free, unlimited uploads and downloads on the pricing page, no published egress meter. That is attractive when the problem is “ship safe uploads this week,” not “own a bucket I can move anywhere.”
The docs are explicit: do not rely on raw storage-provider URLs because files may move between providers. Your callback endpoint must stay publicly reachable. Signed URL expirations cap at seven days.
Integration patterns for Vercel, Netlify, Next.js, and Node
Do not proxy large binary uploads through your hosting function unless the files are tiny. Vercel Functions cap bodies at 4.5 MB. Netlify Functions allow 6 MB buffered payloads, but binary data is Base64-encoded, so the effective binary limit is roughly 4.5 MB again.
Default architecture: browser to object store direct upload, with your server only minting a presigned URL or upload token.
flowchart LR
U[User browser] --> A[App auth/session]
A --> S[Small signer endpoint on Vercel, Netlify, or Node]
S --> T[Presigned URL or upload token]
U --> D[Direct upload to object store or UploadThing ingest]
D --> C[CDN or signed delivery URL]
C --> U | Stack | Pattern | Best-fitting providers |
|---|---|---|
| Vercel | Route Handler signs uploads only; secrets in env vars; browser uploads direct | R2, B2, Storj, Supabase S3, UploadThing, Firebase client SDK |
| Netlify | Function as signer only, not upload proxy | Same as Vercel |
| Next.js | Route Handlers or Server Actions as signer; UploadThing has App Router support; Supabase has Storage quickstarts | UploadThing, Supabase, any S3 backend, Firebase web SDK |
| Generic Node | AWS SDK v3 against S3-compatible endpoints; Firebase Admin for GCS flows | R2, B2, Storj, Supabase S3, Firebase Admin, UploadThing |
| Static sites | Never ship raw S3 secrets to the browser; use signed URLs or a tiny signer | UploadThing, Firebase, Supabase, or public-read R2/B2 with signer for writes |
A generic S3-compatible signer for Next.js or any Node host. Provider differences are mostly endpoint, region, and forcePathStyle (Supabase wants true; R2 uses region auto).
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({
region: process.env.S3_REGION ?? 'auto',
endpoint: process.env.S3_ENDPOINT!,
forcePathStyle: process.env.S3_FORCE_PATH_STYLE === 'true',
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID!,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!
}
});
export async function POST() {
const key = `uploads/${crypto.randomUUID()}.bin`;
const url = await getSignedUrl(
s3,
new PutObjectCommand({
Bucket: process.env.S3_BUCKET!,
Key: key,
ContentType: 'application/octet-stream'
}),
{ expiresIn: 60 }
);
return Response.json({ key, url });
} For Supabase-first apps, uploading through supabase-js under your auth and RLS model is usually simpler than building a separate signer.
const file = input.files?.[0];
const { data, error } = await supabase.storage.from('avatars').upload(`public/${file.name}`, file, {
cacheControl: '3600',
upsert: false
}); For UploadThing, define a file route, auth in middleware, ship with the generated client helpers.
import { createUploadthing, type FileRouter } from 'uploadthing/next';
const f = createUploadthing();
export const ourFileRouter = {
imageUploader: f({ image: { maxFileSize: '4MB', maxFileCount: 1 } })
.middleware(async ({ req }) => {
const user = await auth(req);
if (!user) throw new Error('Unauthorized');
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
return { uploadedBy: metadata.userId, fileKey: file.key };
})
} satisfies FileRouter; Who should pick what
flowchart TD
start[Need object storage] --> egressQ{Viral egress fear?}
egressQ -->|Yes| r2[R2 or UploadThing if managed uploads]
egressQ -->|No| s3Q{Need S3 portability?}
s3Q -->|Yes| b2r2[Backblaze B2 or R2]
s3Q -->|No| authQ{Auth and rules integrated?}
authQ -->|Yes| platform[Supabase or Firebase]
authQ -->|No| nextQ{Next.js upload DX priority?}
nextQ -->|Yes| ut[UploadThing]
nextQ -->|No| b2r2 Hobby projects and solo devs: R2 if the nightmare is bandwidth bill shock. UploadThing if you are on Next.js and want uploads working tonight. B2 if you want the most textbook S3 setup and your download volume is not wildly larger than what you store. Do not treat Firebase as card-free storage; Blaze is required.
Startups: R2 for predictable viral-month bandwidth. B2 behind Cloudflare if you want conventional S3 plus partner egress. Supabase Storage if you are already on Supabase Auth and RLS. UploadThing for TS teams that would rather not own presigned URL plumbing. Firebase only if the rest of the stack is already Firebase.
Teams already on Firebase: Firebase Storage is the ergonomic default. Front with Cloud CDN if GCS egress becomes the line item, and accept that you are now operating CDN cache behavior, not just a bucket.
For a bootstrapped web app I would ship R2, wire direct browser uploads through a small signer endpoint on day one, and keep B2 plus Cloudflare in mind as the first fork if I need fuller S3 parity or already run B2 elsewhere.
Gaps in the public docs
Supabase free-plan docs show bandwidth quotas but not a clear free-plan overage schedule like the paid tables. The egress scenarios above use paid overage rates when modeling what happens after you outgrow free.
Firebase pricing depends on bucket family, region, and destination geography. The numbers here use the current *.firebasestorage.app allowance and GCS general internet transfer as a reference case, not a universal invoice prediction.
Storj’s $50 minimum is on the public pricing page, effective July 1, 2026.
UploadThing does not publish a separate egress or request meter. Bandwidth-risk analysis here follows what the pricing page shows today, not a low-level billing spec.
Sources
Cloudflare R2
R2 pricing
S3 API compatibility
AWS SDK for JavaScript v3
R2 limits
Backblaze B2
B2 pricing
AWS SDK for JavaScript with B2
CDN and compute integrations
Supabase Storage
Storage size usage
Egress usage
S3 authentication
Storage guide
Firebase Storage
Firebase pricing
Cloud Storage for Firebase
Usage limits
Google Cloud Storage pricing
Storj
Storj pricing
S3 compatibility
UploadThing
UploadThing pricing
UploadThing docs
Next.js App Router setup