Object Storage

Scalable, secure storage for files and media
Beta

Object Storage is a scalable, high-performance storage solution for large files and unstructured data in Webflow Cloud. It enables you to store, retrieve, and manage objects, like images, videos, documents, and backups—without the complexity or egress fees of traditional cloud storage.

Use Object Storage when you need to store and serve large, binary, or unstructured data that doesn’t fit well in a database or key-value store.

Storage for Webflow Cloud is in private beta

Visit webflow.com/cloud to request access.

How Object Storage works

Object Storage organizes data as objects, each identified by a unique key within a bucket. Each object can be up to 5GB in size. Buckets are containers for objects and can be configured for public or private access.

Objects are stored and retrieved via a simple API, supporting direct uploads, downloads, and deletions. Data is distributed and replicated for durability and high availability.

Key features:

  • No egress fees - Download your data without extra costs.
  • S3-compatible API - Use familiar tools and SDKs.
  • Scalable and performant - Handles large files and high request volumes.
  • CORS and public buckets - Serve assets directly to the web.

When to use object storage

Object Storage is ideal for:

  • Storing and serving static assets (images, videos, PDF files)
  • Backups and large data exports
  • Data lakes and analytics workloads
  • Any scenario requiring scalable, unstructured storage

Add Object Storage to your app

Create a bucket by adding a binding to the r2_buckets array in your wrangler.json file at the root of your project.

A bucket is a logical container for objects. Think of it as a dedicated database for your app’s files and media. Add a separate binding for each bucket you need for storing and serving files.

After deployment, Webflow Cloud automatically connects your app to each bucket, so you can store and retrieve objects by key.

1

Add a binding to your wrangler.json file

In your wrangler.json file, add a r2_buckets array. Declare a binding for each bucket you want to use inside the array. Binding names must be valid JavaScript variable names.

wrangler.json
1{
2 "r2_buckets": [
3 {
4 "binding": "WEBFLOW_CLOUD_MEDIA",
5 "bucket_name": <YOUR_BUCKET_NAME>, // Replace after deployment
6 }
7 ]
8}
2

Generate types for your binding

Generate TypeScript types for your bindings to enable autocomplete and type safety in your code editor:

$npx wrangler types

This creates/updates a worker-configuration.d.ts file with your binding types. Note: in Next.js you’ll also need to update the types for the cloudflare-env.d.ts file to avoid type errors.

3

Deploy your app

Deploy your app to Webflow Cloud. After deployment, you can view and manage your buckets in the Webflow Cloud dashboard.

Working with Object Storage

Access the binding

Webflow Cloud exposes Object Storage buckets to your app as an environment variable, known as a binding, allowing you to interact with it directly from your application code without the need for API keys and credentials.

Always access the environment variable in your app’s runtime environment. This variable exposes methods from the Cloudflare’s R2 Bindings API, allowing you to run data operations directly from your code.

In Astro, access the binding in your code using the locals object.

src/pages/api/weather.ts
1import type { APIRoute } from "astro";
2import type { R2Bucket } from "@cloudflare/workers-types";
3
4type WeatherCache = { data: any; timestamp: number };
5
6export const GET: APIRoute = async ({ request, locals }) => {
7
8 // Get binding from locals runtime env
9 const env = (locals as any).runtime.env;
10 const kv = env.WEBFLOW_CLOUD_MEDIA as R2Bucket;
11
12// Rest of code...

Data operations

Manage objects in your bucket using the R2 API methods available on the binding. The most common operations are uploading, downloading, deleting, and listing objects. Each method is designed for fast, reliable access to your files and data directly from your app, with no need for external API keys.

Object Storage also supports S3-compatible API operations, making it easy to integrate with existing S3 tools and workflows. Learn more about S3 API compatibility.

Upload an object

To upload (store) an object in your bucket, use the .put() method on your binding. This method stores the provided data under the specified key and creates an R2Object containing the object’s metadata.

.put() returns a promise that resolves to an R2Object with metadata about the stored object. If a precondition in options fails, .put() returns null and the object isn’t stored.

Syntax
1await env.WEBFLOW_CLOUD_MEDIA.put(key, value, options?): Promise<R2Object | null>;
  • key: Unique name for the object (string)
  • value: Data to store (string, ArrayBuffer, ReadableStream, Blob, etc.)
  • options: (Optional) Metadata, HTTP headers, or storage class

Object Storage writes are strongly consistent: once the promise resolves, all subsequent reads will see the new object globally.

For all parameters and advanced options, see the Cloudflare R2 API documentation.

Example
1import type { APIRoute } from "astro";
2import type { R2Bucket } from "@cloudflare/workers-types";
3
4export const PUT: APIRoute = async ({ request, locals }) => {
5
6 // Get binding from locals runtime env
7 const env = (locals as any).runtime.env;
8 const bucket = env.WEBFLOW_CLOUD_MEDIA as R2Bucket;
9
10 const url = new URL(request.url);
11 const key = url.pathname.slice(1); // for example, "uploads/photo.jpg"
12
13 if (request.method === "PUT") {
14
15 // Get the file data from the request body
16 const fileStream = request.body;
17 const contentType = request.headers.get("content-type") || "application/octet-stream";
18
19 // Store the object in the bucket with metadata
20 const result = await bucket.put(key, fileStream, {
21 httpMetadata: { contentType }
22 });
23
24 // Return a JSON response with the object metadata
25 if (result) {
26 return new Response(
27 JSON.stringify({
28 message: `File uploaded successfully.`,
29 key: result.key,
30 size: result.size,
31 uploadedAt: result.uploaded
32 }),
33 { status: 201, headers: { "Content-Type": "application/json" } }
34 );
35 } else {
36 return new Response("Upload failed due to precondition.", { status: 412 });
37 }
38 }
39
40 return new Response("Method Not Allowed", { status: 405, headers: { Allow: "PUT" } });
41}
42};

Download an object

To retrieve an object from your bucket, use the .get() method on your binding. This method fetches the object body and metadata if the key exists, or returns null if not found.

.get() returns a promise that resolves to an R2ObjectBody that contains the object’s data and metadata, or null if the object doesn’t exist.

Syntax
1env.WEBFLOW_CLOUD_MEDIA.get(key, options?): Promise<R2ObjectBody | null>;
  • key: The object key (string)
  • options: (Optional) Conditional headers or response options

For all parameters and advanced options, see the Cloudflare R2 API documentation.

Example
1import type { APIRoute } from "astro";
2import type { R2Bucket } from "@cloudflare/workers-types";
3
4export const GET: APIRoute = async ({ request, locals }) => {
5
6 // Get binding from locals runtime env
7 const env = (locals as any).runtime.env;
8 const bucket = env.WEBFLOW_CLOUD_MEDIA as R2Bucket;
9
10 // Get the object key from the request URL
11 const url = new URL(request.url);
12 const key = url.pathname.slice(1); // for example, "uploads/photo.jpg"
13
14 // Get the object from the bucket
15 const object = await bucket.get(key);
16 if (object) {
17 return new Response(object.body, {
18 headers: { "Content-Type": object.httpMetadata?.contentType || "application/octet-stream" }
19 });
20 } else {
21 return new Response("Not found", { status: 404 });
22 }
23};

Delete an object

To delete an object from your bucket, use the .delete() method on your binding. You can delete a single object by key or multiple objects by passing an array of keys (up to 1000 per call).

.delete() returns a promise that resolves when the objects have been deleted. Deleting a key that doesn’t exist is treated as a successful operation.

Syntax
1env.WEBFLOW_CLOUD_MEDIA.delete(key): Promise<void>;
  • key: The object key (string) or an array of keys

For all parameters and advanced options, see the Cloudflare R2 API documentation.

Example
1import type { APIRoute } from "astro";
2import type { R2Bucket } from "@cloudflare/workers-types";
3
4export const DELETE: APIRoute = async ({ request, locals }) => {
5 // Get binding from locals runtime env
6 const env = (locals as any).runtime.env;
7 const bucket = env.WEBFLOW_CLOUD_MEDIA as R2Bucket;
8 // Get the object key from the request URL
9 const url = new URL(request.url);
10 const key = url.pathname.slice(1); // for example, "uploads/photo.jpg"
11
12 await bucket.delete(key);
13 return new Response("Deleted", { status: 204 });
14};

List objects

To list objects in your bucket, use the .list() method on your binding. This method returns an array of objects and supports options such as prefix filtering, result limits, and pagination.

.list() returns a promise that resolves to an object containing an array of R2Object entries, a truncated boolean indicating if more results are available, and a cursor for pagination.

Syntax
1env.WEBFLOW_CLOUD_MEDIA.list(options?: R2ListOptions): Promise<R2Objects>;
  • options: (Optional) Object with properties like prefix, limit, cursor, and delimiter

For all parameters and advanced options, see the Cloudflare R2 API documentation.

Example
1import type { APIRoute } from "astro";
2import type { R2Bucket } from "@cloudflare/workers-types";
3
4export const GET: APIRoute = async ({ request, locals }) => {
5 // Get binding from locals runtime env
6 const env = (locals as any).runtime.env;
7 const bucket = env.WEBFLOW_CLOUD_MEDIA as R2Bucket;
8 // List all objects with the prefix "images/"
9 const { objects, truncated, cursor } = await bucket.list({ prefix: "images/", limit: 100 });
10 const keys = objects.map(obj => obj.key);
11 return new Response(JSON.stringify({ keys, truncated, cursor }), {
12 headers: { "Content-Type": "application/json" }
13 });
14};

For more information on object storage, see the Cloudflare R2 API documentation.


Back to overview

FAQs

Public buckets aren’t currently supported in Webflow Cloud. To serve files publicly, add them to your public folder.