6 min read

Vercel Blob

Vercel Blob is a scalable, and cost-effective object storage service for static assets, such as images, videos, audio files, and more.
Table of Contents

Vercel Blob is available in Beta on Hobby and Pro plans

Those with the owner, member, developer role can access this feature

Vercel Blob is a great solution for storing blobs that need to be frequently read. Here are some examples suitable for Vercel Blob:

  • Files that are programmatically uploaded or generated at build time, for display and download such as avatars, screenshots, cover images and videos
  • Large files such as videos and audios to take advantage of the global network
  • Files that you would normally store in an external file storage solution like Amazon S3. With your project hosted on Vercel, you can readily access and manage these files with Vercel Blob

You can create and manage your Vercel Blob stores from your account dashboard. You can scope your Vercel Blob stores to your Hobby account or team, and connect them to as many projects as you want.

To get started, see the server-side, or client-side quickstart guides.

If you'd like to know whether or not Vercel Blob can be integrated into your workflow, it's worth knowing the following:

  • You can have one or more Vercel Blob stores per Vercel account
  • You can use multiple Vercel Blob stores in one Vercel project
  • Each Vercel Blob store can be accessed by multiple Vercel projects
  • Vercel Blob URLs are publicly accessible, created with an unguessable random id, and immutable
  • To add to or remove from the content of a Blob store, a valid token is required
  • Making a blob private is planned in an upcoming release

There are two ways to upload files to Vercel Blob:

  1. Server uploads: This is the most common way to upload files. The file is first sent to your server and then to Vercel Blob. It's straightforward to implement, but you are limited to the request body your server can handle. Which in case of a Vercel-hosted website is 4.5 MB. This means you can't upload files larger than 4.5 MB on Vercel when using this method.
  2. Client uploads: This is a more advanced solution for when you need to upload larger files. The file is securely sent directly from the client (a browser for example) to Vercel Blob. This requires a bit more work to implement, but it allows you to upload files up to 5 TB (5,000 GB).

You can also upload files larger than 4.5 MB directly from a script or server code, as long as the file isn't received from a Vercel-hosted website. An example of that would be a server-side fetch() request streaming the response to Vercel Blob.

Vercel Blob URLs, although publicly accessible, are unique and hard to guess. They are composed of a unique store id, a pathname and a unique random blob id generated when the blob is created.

This is similar to Share a file publicly in Google Docs. You should ensure that the URLs are only shared to authorized users

Headers that enhance security by preventing unauthorized downloads, blocking external content from being embedded, and protecting against malicious file type manipulation, are enforced on each blob. They are:

  • content-security-policy: default-src "none"
  • x-frame-options: DENY
  • x-content-type-options: nosniff
  • content-disposition: attachment/inline; filename="filename.extension"

All files stored on Vercel Blob are secured using AES-256 encryption. This encryption process is applied at rest and is transparent, ensuring that files are encrypted before being saved to the disk and decrypted upon retrieval.

Each Blob is served with a content-disposition header. Based on the MIME type of the uploaded file, it is either set to attachment (force file download) or inline (can render in a browser tab). This is done to prevent hosting specific files on @vercel/blob like HTML web pages. Your browser will automatically download the file instead of displaying it for these cases.

Currently text/plain, text/xml, application/json, application/pdf, image/*, audio/* and video/* resolve to a content-disposition: inline header.

All other MIME types default to content-disposition: attachment.

If you need a blob URL that always forces a download you can use the downloadUrl property on the blob object. This URL always has the content-disposition: attachment header no matter its MIME type.

import { list } from '@vercel/blob';
export default async function Page() {
  const response = await list();
  return (
      {response.blobs.map((blob) => (
        <a key={blob.pathname} href={blob.downloadUrl}>

Alternatively the SDK exposes a helper function called getDownloadUrl that returns the same URL.

Vercel Blobs have two levels of caching:

  1. In browsers: Blobs are cached for one year.
  2. In Vercel's edge network: Blobs are cached for 5 minutes.

The 5-minute [edge cache[(/docs/edge-network/caching) is important when you're updating or deleting blobs:

  • If you override (update) a blob, it may take up to 5 minutes for the changes to be visible.
  • If you delete a blob, it may still be accessible for up to 5 minutes.

However, when you create a new blob, it's available immediately without any caching delay, as it doesn't exist in the cache yet.

You can configure this caching behavior by using the cacheControlMaxAge option on the put() method.

The minimum value is 0 second for browser and edge cache. The maximum value is 5 minutes (300 seconds) for edge cache and unlimited for browser cache.

The query string on the blob URL is not part of the cache key, meaning you cannot bypass the cache by adding a unique query string to the URL. To bypass the cache, upload a new version of your file with a different pathname.

Note: when serving blobs from the edge cache, we do no not increment the number of basic operations, we only bill for the used bandwidth.

Vercel Blob has folders support to organize your files:

const blob = await put('folder/file.txt', 'Hello World!', { access: 'public' });

The path folder/file.txt creates a folder named folder and a blob file named file.txt. To list all blobs within a folder, use the list function:

const listOfBlobs = await list({
  limit: 1000,
  prefix: 'folder/',

You don't need to create folders. Upload a file with a path containing a slash /, and Vercel Blob will interpret the slashes as folder delimiters.

In the Vercel Blob file browser on the Vercel dashboard, any pathname with a slash / is treated as a folder. However, these are not actual folders like in a traditional file system; they are used for organizing blobs in listings and the file browser.

Vercel Blob supports range requests for partial downloads. This means you can download only a portion of a blob, here are examples:

curl https://1sxstfwepd7zn41q.public.blob.vercel-storage.com/range-requests.txt
# 0123456789
# First 5 bytes
curl -r 0-4 https://1sxstfwepd7zn41q.public.blob.vercel-storage.com/range-requests.txt
# 01234
# Last 5 bytes
curl -r -5 https://1sxstfwepd7zn41q.public.blob.vercel-storage.com/range-requests.txt
# 56789
# Bytes 3-6
curl -r 3-6 https://1sxstfwepd7zn41q.public.blob.vercel-storage.com/range-requests.txt
# 3456

Every Vercel Blob operation can be canceled, just like a fetch call. This is useful when you want to abort an ongoing operation, for example, when a user navigates away from a page or when the request takes too long.

const abortController = new AbortController();
try {
  const blobPromise = vercelBlob.put('hello.txt', 'Hello World!', {
    access: 'public',
    abortSignal: abortController.signal,
  const timeout = setTimeout(() => {
    // Abort the request after 1 second
  }, 1000);
  const blob = await blobPromise;
  console.info('blob put request completed', blob);
  return blob.url;
} catch (error) {
  if (error instanceof vercelBlob.BlobRequestAbortedError) {
    // Handle the abort
    console.info('canceled put request');
  // Handle other errors

If, for some reason, you want to delete all the blobs in your store, do this:

import { list, del } from '@vercel/blob';
async function deleteAllBlobs() {
  let cursor;
  do {
    const listResult = await list({
      limit: 1000,
    if (listResult.blobs.length > 0) {
      await del(listResult.blobs.map((blob) => blob.url));
    cursor = listResult.cursor;
  } while (cursor);
  console.log('All blobs were deleted');
deleteAllBlobs().catch((error) => {
  console.error('An error occurred:', error);
Last updated on July 13, 2024