TL;DR: Vercel Blob is managed object storage on Amazon S3, wired directly into Vercel's deploy pipeline. For small-to-medium file storage on Vercel apps, it's fast to set up and cheap until you hit ~100 GB or meaningful egress. Past that, the bills get ugly. Here's the real pricing, the things that surprised us in production, and when to just use S3 or Supabase Storage instead.

What Is Vercel Blob?

Vercel Blob stores and serves files -- images, videos, PDFs, anything -- from your Vercel app. It's S3 underneath, which means 99.999999999% durability (the "11 nines" stat where a billion objects stored for 100 years would statistically lose zero files).

The hook is zero config. Install @vercel/blob (0.27.x as of mid-2025), call put(), get back a CDN URL. No IAM roles, no CORS wrestling, no CloudFront distribution setup.

We went GA with Blob in early 2025. It works on Hobby, Pro, and Enterprise plans. The SDK runs on Next.js, SvelteKit, Nuxt -- anything that compiles to Vercel serverless functions.

How Does Vercel Blob Work Under the Hood?

Files go to S3. Delivery happens through two networks:

  1. Fast Data Transfer -- 94 cities globally, optimized for sub-100ms latency. This serves your Next.js pages and API routes.
  2. Blob Data Transfer -- 18 regional hubs, volume-optimized. This serves your blob assets at a lower per-GB rate.

That split matters for cost. When someone downloads a 50 MB video, it routes through Blob Data Transfer at $0.06/GB instead of burning your Fast Data Transfer quota.

Every file gets a URL like this:

https://<store-id>.public.blob.vercel-storage.com/<filename>-<random-suffix>.<ext>

That random suffix creates problems. We'll get to it.

What Does Vercel Blob Cost? Real Pricing Math

Four pricing components on Pro (June 2025):

Component Pro Plan Included Overage Cost
Storage 100 GB $0.023/GB/month
Simple Operations (PUT, COPY, POST, LIST) 100,000/month $0.005 per 1,000
Advanced Operations (GET, HEAD) 1,000,000/month $0.004 per 10,000
Blob Data Transfer (egress) 1 TB/month $0.06/GB

Hobby gives you scraps: 500 MB storage, 1,000 simple ops, 10,000 advanced ops, 1 GB transfer.

Real scenario: 500 users upload a 5 MB profile photo monthly, each photo viewed 200 times

  • Storage: 500 × 5 MB = 2.5 GB. Under 100 GB. $0.
  • PUTs: 500 uploads. Under 100k. $0.
  • GETs: 500 × 200 = 100,000 reads. Under 1M. $0.
  • Transfer: 100,000 × 5 MB = 488 GB. Under 1 TB. $0.

Total overage: $0. The $20/month Pro seat covers this entirely.

Now: 10 GB daily uploads, 50 TB monthly egress

  • Storage after month one: ~300 GB. 200 GB overage × $0.023 = $4.60/month.
  • Transfer: 50 TB - 1 TB = 49 TB overage. 49,000 GB × $0.06 = $2,940/month.

Egress kills you. At 50 TB/month, you're paying $3k just for bandwidth. This is where S3 + CloudFront or Cloudflare R2 (zero egress fees) starts making sense.

Public vs Private Blobs -- What's the Difference?

Public blobs: anyone with the URL reads it. Private blobs: need a token.

We launched with public-only. Private came later (now GA). Here's the breakdown:

Aspect Public Blob Private Blob
Access URL = access Requires BLOB_READ_WRITE_TOKEN or signed URL
Use case Marketing assets, avatars User docs, sensitive uploads
CDN caching Cached at edge Not cached by default
URL format *.public.blob.vercel-storage.com *.private.blob.vercel-storage.com
SDK access mode access: 'public' access: 'private'

When we use private blobs

Anything user-specific that shouldn't leak -- invoices, medical PDFs, multi-tenant SaaS uploads. The URL alone shouldn't grant access.

Pattern we run: store as private blob, generate a short-lived download URL from an API route after checking auth. Same idea as S3 presigned URLs.

import { getDownloadUrl } from '@vercel/blob';

// In your API route, post-auth
const downloadUrl = await getDownloadUrl(blobUrl);
// Expires after set duration

The Gotchas Nobody Tells You About

We've shipped Vercel Blob in four client projects. Here's what hurt:

1. The random filename suffix

When you call put('avatar.png', file, { access: 'public' }), you get back avatar-a1b2c3d4e5f6.png. Vercel appends a random suffix to guarantee uniqueness.

This means:

  • You can't predict the URL pre-upload.
  • You must store the returned URL in your database.
  • Clean, predictable URLs like /uploads/user-42/avatar.png don't exist out of the box.

You can disable it:

const blob = await put('avatars/user-42.png', file, {
  access: 'public',
  addRandomSuffix: false,
});

But now you risk overwriting files with the same name. Docs recommend against this for most cases. It's a real trade-off.

2. No built-in image transformations

Vercel Blob stores files. It doesn't resize, crop, or convert them. If you need image processing, pair it with next/image (which costs $5 per 1,000 source images on Pro) or use Cloudinary.

Cloudinary wrote a solid piece on uploading images with Vercel serverless functions that pairs Vercel compute with their transformation pipeline. For on-the-fly resizing, watermarking, or WebP/AVIF conversion, a dedicated image CDN makes more sense than raw blob storage. We covered this in our Cloudinary alternatives guide.

3. Cache invalidation is manual

Public blob URLs cache at the edge. Overwrite a file (with addRandomSuffix: false) and the old version may serve for a while. There's no purge API for blob URLs.

Workaround: leave the random suffix on (default), treat every upload as a new file, update the DB reference. Delete old files explicitly with del(oldUrl).

4. The 500 MB body size limit

Serverless functions on Vercel cap request bodies. For server-side uploads (put() from a Route Handler), max payload is 4.5 MB on Hobby and Pro. Bigger files need the client-side upload pattern, which streams directly to Blob storage via a tokenized upload URL.

Client-side uses upload() from @vercel/blob/client and bypasses the function body limit. Max file size: 500 MB (5 TB on Enterprise).

5. Local development hits production

No local blob emulator. next dev hits the real Vercel Blob API. You need a real BLOB_READ_WRITE_TOKEN in dev, and every test upload counts against your quota. The team mentioned fixing this; as of mid-2025, it's still the case.

How to Upload Files with Vercel Blob

Two patterns. For deeper coverage -- validation, progress bars, error handling -- see our Next.js file upload guide.

Server-side upload (< 4.5 MB)

// app/api/upload/route.ts
import { put } from '@vercel/blob';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const formData = await request.formData();
  const file = formData.get('file') as File;

  if (!file) {
    return NextResponse.json({ error: 'No file' }, { status: 400 });
  }

  const blob = await put(file.name, file, {
    access: 'public',
  });

  // blob.url => CDN URL to store in DB
  // blob.pathname => path in blob storage
  // blob.contentType => MIME type
  // blob.contentDisposition => attachment or inline

  return NextResponse.json(blob);
}

Client-side upload (up to 500 MB)

// app/api/upload/route.ts -- token handler
import { handleUpload, type HandleUploadBody } from '@vercel/blob/client';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const body = (await request.json()) as HandleUploadBody;

  const jsonResponse = await handleUpload({
    body,
    request,
    onBeforeGenerateToken: async (pathname) => {
      // Auth check here
      return {
        allowedContentTypes: ['image/jpeg', 'image/png', 'image/webp'],
        maximumSizeInBytes: 10 * 1024 * 1024, // 10 MB
      };
    },
    onUploadCompleted: async ({ blob }) => {
      // Save blob.url to DB
      console.log('Upload done:', blob.url);
    },
  });

  return NextResponse.json(jsonResponse);
}
// Client component
'use client';
import { upload } from '@vercel/blob/client';

export function UploadForm() {
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const file = (e.currentTarget.elements.namedItem('file') as HTMLInputElement).files?.[0];
    if (!file) return;

    const blob = await upload(file.name, file, {
      access: 'public',
      handleUploadUrl: '/api/upload',
    });

    console.log('Uploaded:', blob.url);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="file" name="file" required />
      <button type="submit">Upload</button>
    </form>
  );
}

When Is S3 Cheaper Than Vercel Blob?

S3 undercuts Vercel Blob almost any time you're paying overages. The crossover is when you exceed Pro plan allowances.

500 GB storage, 5 TB monthly egress:

Cost Component Vercel Blob (Pro) AWS S3 + CloudFront Cloudflare R2
Storage (500 GB) 400 GB × $0.023 = $9.20 500 GB × $0.023 = $11.50 500 GB × $0.015 = $7.50
Egress (5 TB) 4 TB × $0.06/GB = $245.76 ~$0.085/GB avg = $435 $0 (free egress)
Operations ~$5 ~$5 ~$4.50
Monthly total ~$260 ~$452 ~$12

Vercel Blob beats raw S3 + CloudFront at 5 TB egress because Blob Data Transfer ($0.06/GB) undercuts CloudFront's standard rate (~$0.085/GB). But R2 obliterates both at $12/month because egress is free.

Real answer: if egress drives your cost, Cloudflare R2 wins. If your usage fits inside Pro plan allowances, Vercel Blob is effectively free. The danger zone is the middle -- too much egress for free tier, not enough to justify migrating to R2.

For the self-serve S3 path, factor in engineering time: IAM policies, CORS, presigned URL logic, CloudFront distribution, cache invalidation. On a recent client project, production-ready S3 + Lambda@Edge auth + CloudFront took 12 hours. Vercel Blob took 45 minutes.

When Is Supabase Storage a Better Fit?

Supabase Storage makes sense when you're already using Supabase for DB and auth, and you want file storage that respects your Row Level Security policies.

Supabase's storage v2 -- like Vercel Blob -- sits on S3. But there are real differences:

Feature Vercel Blob Supabase Storage
Auth integration None (bring your own) Built-in with Supabase Auth + RLS
Image transforms Via next/image only Built-in (resize, format conversion)
Access control Public or private (token-based) Per-file policies via RLS
Pricing model Per GB + egress Per GB (2 GB free, then $0.021/GB)
Egress $0.06/GB after 1 TB Counted against project bandwidth
Max file size 500 MB (5 TB Enterprise) 5 GB (Pro)
CDN Vercel Edge (94 cities) Supabase CDN (smart headers)
Framework lock-in Best with Vercel apps Framework-agnostic

Pick Supabase Storage when:

  1. You're already on Supabase. Postgres + Supabase Auth + Storage slots in with zero extra auth wiring. RLS controls file access at the DB level.
  2. You need image transformations without a third party. Supabase has built-in resize and WebP conversion. Vercel Blob doesn't.
  3. You're not on Vercel. Vercel Blob needs a Vercel account and works best when deployed there. Supabase Storage works anywhere.

Pick Vercel Blob when:

  1. Your app is on Vercel and you want the fastest path. Install SDK, set env var, done.
  2. You need tight next/image integration. Vercel Blob URLs work natively with Image Optimization.
  3. You want Vercel's edge network. The 94-city Fast Data Transfer network and 18-hub Blob Data Transfer network give you global latency you can't replicate easily.

Vercel Blob vs S3 vs Supabase Storage Comparison

Criteria Vercel Blob AWS S3 (direct) Cloudflare R2 Supabase Storage
Setup time ~10 min ~2-4 hours ~30 min ~20 min
Storage cost/GB $0.023 $0.023 $0.015 $0.021
Egress cost/GB $0.06 $0.09 (direct) $0 Varies by plan
Built-in CDN Yes (18 hubs) No (add CloudFront) Yes (global) Basic
Image transforms No (use next/image) No (add Lambda) No Yes (built-in)
Auth integration Token-based IAM/presigned Token/presigned RLS policies
Max file size 500 MB 5 TB 5 GB per part 5 GB
Local dev Hits production API LocalStack/MinIO Wrangler local Supabase CLI
Vendor lock-in High (Vercel) Low Medium Medium

FAQ

Is Vercel Blob free?

Hobby plan: 500 MB storage, 1 GB transfer, limited ops. Fine for prototypes. Production needs Pro ($20/month/member): 100 GB storage, 1 TB transfer.

Can I use Vercel Blob without deploying to Vercel?

@vercel/blob SDK requires a BLOB_READ_WRITE_TOKEN from a Vercel project. You need a Vercel account and connected project. You can call the API from anywhere, but the project setup is mandatory.

Does Vercel Blob support versioning?

No. Unlike S3, there's no object versioning. Overwrite a file (with addRandomSuffix: false) and the previous version is gone. Default random suffix behavior sidesteps this by making every upload unique.

How do I delete files?

del() from @vercel/blob. Pass the blob URL. Batch deletion: pass an array. There's also a dashboard UI for manual management.

Can I set custom headers or metadata?

You can set cacheControlMaxAge, contentType, and contentDisposition during upload. Custom metadata beyond these isn't supported as of mid-2025.

What happens if I exceed limits?

Pro: automatic overage charges. Hobby: hard cap -- uploads fail once you hit limits. Vercel sends alerts at 50%, 75%, 90% thresholds.

Is Vercel Blob GDPR compliant?

Blob stores data in your selected region (20 regions available at GA). For GDPR, pick an EU region. The CDN may cache from edge nodes outside the EU -- consider this for strict data residency.

Can I migrate from Vercel Blob to S3 later?

Yes, but you re-upload files or script a migration. Vercel Blob URLs are Vercel-specific, so you'd update all DB references. No built-in export -- use list() and getDownloadUrl() APIs to iterate and download.