Cloudflare Docs
Edit this page on GitHub
Set theme to dark (⇧+D)

Securely access and upload assets with Cloudflare R2

This tutorial explains how to create a TypeScript-based Cloudflare Workers project that can securely access files from and upload files to a Cloudflare R2 bucket. Cloudflare R2 allows developers to store large amounts of unstructured data without the costly egress bandwidth fees associated with typical cloud storage services.

​​ Prerequisites

To continue:

  1. Sign up for a Cloudflare account if you have not already.
  2. Install npm.
  3. Install Node.js. Use a Node version manager like Volta or nvm to avoid permission issues and change Node.js versions. Wrangler requires a Node version of 16.17.0 or later.

​​ Create a Worker application

First, use the create-cloudflare CLI to create a new Worker. To do this, open a terminal window and run the following command:

$ npm create cloudflare@latest
$ yarn create cloudflare

To continue with this guide:

  1. Give your new Worker application a name.
  2. Select "Hello World" Worker for the type of application.
  3. Select Yes to using TypeScript.
  4. Select No to deploying your application.

If you choose to deploy, you will be asked to authenticate (if not logged in already), and your project will be deployed. If you deploy, you can still modify your Worker code and deploy again at the end of this tutorial.

​​ Create an R2 bucket

Before you integrate R2 bucket access into your Worker application, an R2 bucket must be created:

$ npx wrangler r2 bucket create <YOUR_BUCKET_NAME>

Replace <YOUR_BUCKET_NAME> with the name you want to assign to your bucket. List your account’s R2 buckets to verify that a new bucket has been added:

$ npx wrangler r2 bucket list

​​ Configure access to an R2 bucket

After your new R2 bucket is ready, use it inside your Worker application.

Use your R2 bucket inside your Worker project by modifying the wrangler.toml configuration file to include an R2 bucket binding. Add the following R2 bucket binding to your wrangler.toml file:

binding = 'MY_BUCKET'
bucket_name = '<YOUR_BUCKET_NAME>'

Give your R2 bucket binding name. Replace <YOUR_BUCKET_NAME> with the name of the R2 bucket you created earlier.

Your Worker application can now access your R2 bucket using the MY_BUCKET variable. You can now perform CRUD (Create, Read, Update, Delete) operations on the contents of the bucket.

​​ Fetch from an R2 bucket

After setting up an R2 bucket binding, you will implement the functionalities for the Worker to interact with the R2 bucket, such as, fetching files from the bucket and uploading files to the bucket.

To fetch files from the R2 bucket, use the BINDING.get function. In the below example, the R2 bucket binding is called MY_BUCKET. Using .get(key), you can retrieve an asset based on the URL pathname as the key. In this example, the URL pathname is /image.png, and the asset key is image.png.

export default {
async fetch(request, env: any) {
// For example, the request URL
const url = new URL(request.url);
const key = url.pathname.slice(1);
// Retrieve the key "image.png"
const object = await env.MY_BUCKET.get(key);
if (object === null) {
return new Response('Object Not Found', { status: 404 });
const headers = new Headers();
headers.set('etag', object.httpEtag);
return new Response(object.body, {
} satisfies ExportedHandler;

The code written above fetches and returns data from the R2 bucket when a GET request is made to the Worker application using a specific URL path.

​​ Upload securely to an R2 bucket

Next, you will add the ability to upload to your R2 bucket using authentication. To securely authenticate your upload requests, use Wrangler’s secret capability. Wrangler was installed when you ran the create cloudflare@latest command.

Create a secret value of your choice – for instance, a random string or password. Using the Wrangler CLI, add the secret to your project as AUTH_SECRET:

$ npx wrangler secret put AUTH_SECRET

Now, add a new code path that handles a PUT HTTP request. This new code will check that the previously uploaded secret is correctly used for authentication, and then upload to R2 using MY_BUCKET.put(key, data):

export default {
async fetch(request, env: any) {
if (request.method === 'PUT') {
// Note that you could require authentication for all requests
// by moving this code to the top of the fetch function.
const auth = request.headers.get('Authorization');
const expectedAuth = `Bearer ${env.AUTH_SECRET}`;
if (!auth || auth !== expectedAuth) {
return new Response('Unauthorized', { status: 401 });
const url = new URL(request.url);
const key = url.pathname.slice(1);
await env.MY_BUCKET.put(key, request.body);
return new Response(`Object ${key} uploaded successfully!`);
// include the previous code here...
} satisfies ExportedHandler;

This approach ensures that only clients who provide a valid bearer token, via the Authorization header equal to the AUTH_SECRET value, will be permitted to upload to the R2 bucket. If you used a different binding name than AUTH_SECRET, replace it in the code above.

​​ Deploy your Worker application

After completing your Cloudflare Worker project, deploy it to Cloudflare. Make sure you are in your Worker application directory that you created for this tutorial, then run:

$ npx wrangler deploy

Your application is now live and accessible at <YOUR_WORKER>.<YOUR_SUBDOMAIN>

You have successfully created a Cloudflare Worker that allows you to interact with an R2 bucket to accomplish tasks such as uploading and downloading files. You can now use this as a starting point for your own projects.

​​ Next steps

To build more with R2 and Workers, refer to Tutorials and the R2 documentation.

If you have any questions, need assistance, or would like to share your project, join the Cloudflare Developer community on Discord to connect with fellow developers and the Cloudflare team.