# The Complete Guide to Vercel Blob

**Author:** Ben Sabic

---

[Vercel Blob](https://vercel.com/storage/blob) is an object storage service for uploading files at build time or at runtime, such as when users submit avatars, documents, or videos. Each file gets a unique URL, and Vercel's CDN caches and delivers it globally without a separate CDN or storage provider. Blob is available on all plans, including a free allowance on Hobby, and stores can be public or private depending on how you want files accessed.

In this guide, you'll learn how Vercel Blob stores and delivers files and how to create a Blob store for your project. You'll upload files from your server and directly from the browser, serve public and private blobs to your users, and troubleshoot the most common upload and caching problems.

## How does Vercel Blob work?

Vercel Blob stores your files as "blobs" and serves them through Vercel's CDN, which caches blobs for up to one month by default. Blob has two parts that work together:

- Blob store: The storage container that holds your files. Each store is either public or private, and you cannot change its access mode after creating it.
  
- The `@vercel/blob` SDK: The npm package that uploads, lists, copies, and deletes blobs from your server or client code.
  

When you upload a file, Vercel Blob returns a URL in the form `https://<store-id>.public.blob.vercel-storage.com/<pathname>` for public stores, or a `.private.` equivalent for private stores. Public blob URLs are accessible to anyone with the link, and the browser fetches them directly from the CDN. Private blobs require authentication for every read and write, so you serve them through a Vercel Function that authenticates the request, fetches the blob with `get()`, and streams the response.

Delivery uses [Blob Data Transfer](https://vercel.com/docs/vercel-blob#blob-data-transfer), which serves content from 20 regional hubs and is, on average, around 3x more cost-efficient than Fast Data Transfer. This makes Blob well-suited for large media files like images, videos, and documents.

## Plans and pricing for Vercel Blob

Vercel Blob is available on all plans. Hobby users can use Blob for free within [usage limits;](https://vercel.com/docs/vercel-blob/usage-and-pricing) Pro teams pay for usage through their monthly credit allocation before switching to on-demand pricing; and Enterprise pricing is custom through your account team.

You're billed for storage, operations, uploads, and data transfer. Private and public stores are priced the same for storage, operations, and uploads. The difference is delivery: public blobs incur Blob Data Transfer on cache misses, while private blobs also incur Fast Data Transfer because your Function streams the response to the browser. Blob Data Transfer fees apply only to downloads, not uploads.

## Before you begin

Before you set up Vercel Blob, make sure you have:

- A [Vercel account](https://vercel.com/signup).
  
- The `@vercel/blob` package installed:
  

- A decision on the access mode. Choose public for assets anyone can view, such as product images, or private for sensitive documents and user content. You cannot change the access mode after creating the store.
  

## How to create a Blob store

Create a Blob store from the Vercel dashboard or the CLI. The dashboard walks you through each option, while the CLI is faster if your project is already linked.

### Create a store from the dashboard

From your project's [Storage tab](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fstores), select **Create Database**, then choose **Blob** and follow the setup flow.

Two settings cannot be changed after creation, so choose them carefully: the access mode (public or private) and the region. Pick a region close to your users and Functions to minimize upload time.

Because you created the store within your project, Vercel automatically connects it to that project. No further setup is needed.

### Create a store from the CLI

If your project is linked with `vercel link`, create a store with one command. The `--access` flag is required:

`vercel blob create-store my-blob-store --access public`

### What Vercel configures

Connected stores use [OpenID Connect (OIDC) authentication](https://vercel.com/docs/oidc) by default, which is more secure than a static credential. Vercel adds two environment variables to the connected project:

- `BLOB_STORE_ID`: The ID of your Blob store. The SDK pairs it with the OIDC token to authenticate requests.
  
- `BLOB_WEBHOOK_PUBLIC_KEY`: The public key the SDK uses to verify webhook callbacks signed by Vercel Blob when uploads happen through presigned URLs.
  

The OIDC token itself is not a stored environment variable. Vercel generates a fresh token for each build and provides it to your functions at runtime, and the SDK picks it up automatically. Because the token is short-lived and rotates on its own, there's no long-lived secret that can leak from your codebase or environment.

For local development, pull your environment variables into your project. This writes a short-lived OIDC token to `.env.local` so the SDK can authenticate locally:

`vercel env pull`

### Connect the store to additional projects

Vercel Blob stores can serve multiple projects. To connect an existing store to another project, go to your [team’s stores](https://vercel.com/d?to=%2F%5Bteam%5D%2F~%2Fstores) and select the one you created, then connect another project on the **Projects** tab.

## How to upload files from your server

Upload files from a Route Handler, API Route, or Server Action using the `put()` method. Server uploads work well for files under 4.5 MB, which is the request body size limit on Vercel Functions.

`import { put } from '@vercel/blob'; export async function POST(request: Request) { const form = await request.formData(); const file = form.get('file') as File; const blob = await put(file.name, file, { access: 'public', // or 'private' addRandomSuffix: true, }); return Response.json(blob); }`

The response includes the blob's `url`, `downloadUrl`, `pathname`, `contentType`, and `etag`. To organize files, include a folder in the pathname, such as `avatars/user-42.png`.

For files larger than 4.5 MB, upload from the browser instead using client uploads, or set `multipart: true` for files over 100 MB. Multipart uploads split the file into parts, upload them in parallel, retry failed parts, and support files up to 5 TB.

## How to upload files directly from the browser

Client uploads send the file from the browser straight to Vercel Blob, bypassing the 4.5 MB Function body limit. The upload is secured by a token exchange between your server and Vercel Blob.

First, create a server route that generates client tokens with `handleUpload()`:

`import { handleUpload, type HandleUploadBody } from '@vercel/blob/client'; import { NextResponse } from 'next/server'; export async function POST(request: Request): Promise<NextResponse> { const body = (await request.json()) as HandleUploadBody; try { const jsonResponse = await handleUpload({ body, request, onBeforeGenerateToken: async (pathname) => { // Authenticate and authorize users here. // Without this check, anyone can upload to your Blob store. return { allowedContentTypes: ['image/jpeg', 'image/png', 'image/webp'], addRandomSuffix: true, }; }, onUploadCompleted: async ({ blob, tokenPayload }) => { // Called by Vercel Blob when the upload completes. // Update your database with blob.url here. }, }); return NextResponse.json(jsonResponse); } catch (error) { return NextResponse.json({ error: (error as Error).message }, { status: 400 }); } }` Then call `upload()` from your client code with the route as the `handleUploadUrl`: `import { upload } from '@vercel/blob/client'; const blob = await upload(file.name, file, { access: 'public', // or 'private' handleUploadUrl: '/api/avatar/upload', });` You must authenticate users in `onBeforeGenerateToken` before generating a token. Without that check, your route allows anonymous uploads to your store. Note that `handleUpload` always requires a read-write token to sign client tokens, so keep `BLOB_READ_WRITE_TOKEN` configured even when your other code uses OIDC. Client uploads also have no data transfer charges, since the file never passes through your Function. ## How to serve and manage blobs How you serve a blob depends on the store's access mode. For public stores, use the blob URL directly in your HTML or with `next/image`: `import Image from 'next/image'; <Image src="https://your_store_id_here.public.blob.vercel-storage.com/avatar.png" alt="User avatar" width={200} height={200} />` Add the store's domain to `remotePatterns` in your Next.js config first. Appending `?download=1` to any blob URL forces a file download instead of displaying the file in the browser. For private stores, create a route that authenticates the request, fetches the blob with `get()`, and streams the response: `import { get } from '@vercel/blob'; export async function GET(request: Request) { // Authenticate the request before serving the file. const { searchParams } = new URL(request.url); const pathname = searchParams.get('pathname'); const result = await get(pathname, { access: 'private' }); return new Response(result.stream); }` To manage existing blobs, the SDK provides `list()`, `copy()`, and `del()`. You can also manage a store from the CLI with commands like `vercel blob list --prefix images/` and `vercel blob del images/old-logo.png`. ## Troubleshooting ### Uploads fail with a 413 FUNCTION\_PAYLOAD\_TOO\_LARGE error Your file exceeds the 4.5 MB request body limit on Vercel Functions. Switch to client uploads so the file goes directly from the browser to Vercel Blob without passing through your Function. ### The browser serves an old version after you update a blob Vercel's cache updates within 60 seconds of a delete or overwrite, but browsers keep serving their cached copy until it expires. Add a unique query parameter to the blob URL, such as `?v=2`, to force browsers to fetch the updated content. For frequently changing files, lower the cache duration with the `cacheControlMaxAge` option when uploading. The minimum is 60 seconds. ### The onUploadCompleted callback doesn't fire in local development Vercel Blob can't reach your localhost, so the callback never arrives. Run your local application through a tunneling service like ngrok, then set the `VERCEL_BLOB_CALLBACK_URL` environment variable in `.env.local` to your tunnel URL. ### Anyone can upload files to your store Your client upload route is missing authentication. Verify the user's session inside `onBeforeGenerateToken` and throw an error if they aren't authenticated. Also restrict `allowedContentTypes` to the file types you expect. ## Related resources and next steps - Learn how Blob fits into Vercel's storage options in the [Storage overview](https://vercel.com/docs/storage).
  
- Explore every SDK method in the [@vercel/blob reference](https://vercel.com/docs/vercel-blob/using-blob-sdk).
  
- Review [Vercel Blob pricing](https://vercel.com/docs/vercel-blob/usage-and-pricing) to estimate your costs.

---

[View full KB sitemap](/kb/sitemap.md)
