I'm trying to list objects in a Cloudflare R2 storage bucket using the AWS SDK for JavaScript (v3) in a Node.js application, but I'm encountering an 'Unauthorized' error.
Here is the code snippet that I'm using:
const { S3Client, ListObjectsCommand } = require('@aws-sdk/client-s3');
const s3Client = new S3Client({
endpoint: 'https://<my-r2-subdomain>.r2.cloudflarestorage.com',
credentials: {
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
},
signatureVersion: 'v4',
});
const bucketParams = {
Bucket: 'my-bucket-name',
};
const listObjects = async () => {
try {
const data = await s3Client.send(new ListObjectsCommand(bucketParams));
console.log('Success', data);
} catch (err) {
console.error('Error', err);
}
};
listObjects();
I receive the following 401 response:
{
"$fault": "client",
"$metadata": {
"httpStatusCode": 401,
"requestId": "undefined",
"extendedRequestId": "undefined",
"cfId": "undefined",
"attempts": 1,
"totalRetryDelay": 0
},
"Code": "Unauthorized"
}
The error stack trace is:
Error Unauthorized: Unauthorized
at throwDefaultError (D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\smithy-client\dist-cjs\default-error-handler.js:8:22)
at D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\smithy-client\dist-cjs\default-error-handler.js:18:39
at de_ListObjectsCommandError (D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\client-s3\dist-cjs\protocols\Aws_restXml.js:5032:20)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\middleware-serde\dist-cjs\deserializerMiddleware.js:7:24
at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-signing\dist-cjs\awsAuthMiddleware.js:14:20
at async D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\middleware-retry\dist-cjs\retryMiddleware.js:27:46
at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-sdk-s3\dist-cjs\region-redirect-endpoint-middleware.js:14:24
at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-sdk-s3\dist-cjs\region-redirect-middleware.js:9:20
at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-logger\dist-cjs\loggerMiddleware.js:7:26
I have confirmed that:
the Cloudflare R2 access key ID & secret access key are correct and active
the token has admin read & write access to all buckets
no proxies or firewalls are active
the environment variables are correctly loaded into the Node.js process
What is the issue?
You need to specify a region, specifically a CloudFlare R2 region.
CloudFlare R2 regions aren't the same as AWS S3 regions. If you don't manually specify an R2 region for your client, things like using aws configure
, presence of a '~/.aws/config' file, or other configuration options on your machine may lead your client to using an AWS region.
These AWS regions won't be compatible with CloudFlare R2, so you need to make sure that at some point in the credentials provider chain, your AWS SDK client uses an R2 region.
Per CloudFlare docs, valid values are:
auto
: Automatic selection of the closest region based on caller locationwnam
: Western North Americaenam
: Eastern North Americaweur
: Western Europeeeur
: Eastern Europeapac
: Asia-PacificSpecify the region like so:
const s3Client = new S3Client({
endpoint: 'https://${ACCOUNT_ID}.r2.cloudflarestorage.com',
region: "{REGION}",
credentials: {
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
},
signatureVersion: 'v4',
});
As a side-note,
This action has been revised. We recommend that you use the newer version,
ListObjectsV2
, when developing applications. For backward compatibility, Amazon S3 continues to support ListObjects.
Use ListObjectsV2Command
for new applications.