Public Storage
Vercel Blob is available on all plans
Those with the owner, member, developer role can access this feature
Public Blob stores make files accessible to anyone with the URL. Use public storage for images, videos, large media, and public assets where authentication isn't needed.
See differences with private storage.
You can create a public Blob store from the Vercel dashboard or the CLI:
- Dashboard:
- Go to your project's Storage tab
- Select Create Database, then choose Blob
- Select Continue, then set the access to Public
- CLI: Run
vercel blob create-store [name] --access public
Your project needs a BLOB_READ_WRITE_TOKEN environment variable to interact with the store. When you create a Blob store from a project, this variable is added automatically. When you create a store from the team level, you'll need to connect it to a project for the environment variable to be added.
Upload files to a public Blob store using put() with access: 'public':
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',
});
return Response.json(blob);
}See the server uploads and client uploads guides for detailed instructions on uploading files.
Every file uploaded to a public Blob store gets a URL in the form of https://<store-id>.public.blob.vercel-storage.com/<pathname>. You can use this URL directly in your HTML:
<img
src="https://my-store-id.public.blob.vercel-storage.com/avatar.png"
alt="User avatar"
/>To use next/image, add the blob store hostname to your next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
new URL('https://my-store-id.public.blob.vercel-storage.com/**'),
],
},
};
module.exports = nextConfig;Then use Image instead of img:
import Image from 'next/image';
<Image
src="https://my-store-id.public.blob.vercel-storage.com/avatar.png"
alt="User avatar"
width={200}
height={200}
/><a href="https://my-store-id.public.blob.vercel-storage.com/report.pdf?download=1">
Download report
</a>Adding ?download=1 to a blob URL forces a file download, regardless of file type. The SDK also exposes this as the downloadUrl property on blob objects. Without it, the URL displays the file in the browser when the MIME type supports it (images, videos, audio, PDFs, plain text, XML, and JSON). All other MIME types automatically trigger a download.
This behavior is controlled by the content-disposition header. Vercel Blob sets it to inline for displayable types and attachment for everything else. This also prevents hosting HTML pages on Vercel Blob.
When you request a public blob URL, the content is cached in two places:
- Vercel's CDN cache
- Your browser's cache
Both caches store blobs for up to 1 month by default to ensure optimal performance when serving content. While both systems aim to respect this duration, blobs may occasionally expire earlier.
Vercel will cache blobs up to 512 MB. Bigger blobs will always be served from the origin (your store).
You can customize the caching duration using the cacheControlMaxAge option in the put() and handleUpload methods.
The minimum configurable value is 60 seconds (1 minute). This represents the maximum time needed for our cache to update content behind a blob URL. For applications requiring faster updates, consider using a Vercel function instead.
See caching best practices on the overview page for guidance on updating and overwriting blobs.
When a browser requests a public blob URL, the CDN automatically includes an ETag header in the response. On subsequent requests, the browser sends an If-None-Match header with the cached ETag. If the blob hasn't changed, the CDN responds with 304 Not Modified and the browser uses its cached copy, avoiding a full re-download.
This works automatically for public blobs accessed by URL. You don't need to write any code to benefit from conditional requests.
If you're using get() to process public blobs server-side, you can also pass the ifNoneMatch option to avoid re-downloading unchanged blobs:
import { get } from '@vercel/blob';
const result = await get('images/hero.png', {
access: 'public',
ifNoneMatch: previousEtag,
});
if (result.statusCode === 304) {
// Blob hasn't changed, use the version you already have
}While Vercel Blob URLs can be designed to be unique and unguessable (when using addRandomSuffix: true), they can still be indexed by search engines under certain conditions:
- If you link to blob URLs from public webpages
- If you embed blob content (images, PDFs, etc.) in indexed content
- If you share blob URLs publicly, even in contexts outside your application
By default, Vercel Blob does not provide a robots.txt file or other indexing controls. This means search engines like Google may discover and index your blob content if they find links to it.
If you want to prevent search engines from indexing your blob content, you need to upload a robots.txt file directly to your blob store:
- Go to your Storage page and select your blob store
- Upload a
robots.txtfile to the root of your blob store with appropriate directives
Example robots.txt content to block all crawling of your blob store:
User-agent: *
Disallow: /
If your blob content has already been indexed by search engines:
- Verify your website ownership in Google Search Console
- Upload a
robots.txtfile to your blob store as described above - Use the "Remove URLs" tool in Google Search Console to request removal
When a public blob is downloaded, you pay for:
- Browser fetches the blob: Blob Data Transfer + Fast Origin Transfer on cache miss
Blob Data Transfer is 3x more cost-efficient than Fast Data Transfer on average, making public storage ideal for large media files like images, videos, and documents.
See pricing documentation for full details.
Upload charges depend on your implementation method:
- Client Uploads: No data transfer charges for uploads
- Server Uploads: Fast Data Transfer charges apply when your Vercel application receives the file
See pricing documentation for full details.
Was this helpful?