← All posts

Using Cloudflare R2 from a Mac with an S3-compatible client

Cloudflare R2 implements the Amazon S3 API, so you don't need an R2-specific tool to work with it from a Mac. Any S3 client that lets you set a custom endpoint will do: point it at https://<ACCOUNT_ID>.r2.cloudflarestorage.com, set the region to auto, and sign in with an R2 API token's access key pair. This guide covers what a Cloudflare R2 client on a Mac needs, the mistakes that produce confusing errors, and a worked setup.

What R2 needs from an S3 client

An S3 request is an HTTPS call to a hostname, signed with AWS Signature Version 4. To talk to R2 instead of Amazon, a client has to change three things:

Get those three right and listing, uploading, downloading and deleting work as they would against Amazon S3.

Creating an R2 API token

In the Cloudflare dashboard, open R2 and find the option to manage API tokens. Create a token and choose its permissions with care:

When you create the token, Cloudflare shows an Access Key ID and a Secret Access Key. The secret is shown once. Copy it straight into your client (or a password manager) before closing the page; if you lose it, you create a new token rather than recovering the old one.

Path-style or virtual-hosted addressing

S3 clients can put the bucket name in the hostname (my-bucket.s3.amazonaws.com, called virtual-hosted style) or in the path (s3.amazonaws.com/my-bucket, path style). Amazon prefers the hostname form; its virtual hosting documentation explains the difference.

For R2, path style is the safe choice: the request goes to <ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/key, the hostname that R2's own examples use. It also avoids a certificate problem that bites with any S3 service: a bucket name containing dots, such as assets.example.com, breaks the wildcard TLS certificate when it is placed in the hostname.

Checking the setup from the terminal first

If something fails in a graphical client, it helps to know whether the problem is the credentials or the client. The AWS CLI works with R2 when you give it the endpoint:

aws configure --profile r2
# Access key ID and secret from the R2 token; region: auto

aws s3 ls s3://my-bucket \
  --profile r2 \
  --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com

If that lists your objects, the token and endpoint are right, and any remaining problem is in how the other client is configured.

Common errors and what they mean

SymptomUsual cause
Signature does not matchThe secret was pasted with a trailing space or newline, or the region isn't auto.
Access deniedThe token is read-only, or scoped to a different bucket.
Hostname or certificate errorThe bucket ended up in the hostname, or the endpoint includes the bucket name as well.
Bucket not foundThe bucket name was typed as a URL, such as s3://my-bucket or with a trailing slash.

Setting up Wharf as a Cloudflare R2 client on your Mac

Wharf talks to Amazon S3 and S3-compatible storage through the same site editor as SFTP and FTP, and names R2 as one of the services the custom endpoint is for. Add a site with the + button and fill it in like this:

When you connect, Wharf makes one small listing request against the bucket, so a wrong key, region or bucket name shows up straight away rather than on your first upload. It never asks R2 to list all your buckets, which means a token scoped to a single bucket works fine.

If a request is refused, View › Server Log… (⌘⇧K) shows each S3 request and R2's response, including the error body. For signature mismatches it also records the canonical request and string to sign, which you can compare against what R2 sends back. The secret itself is never written to the log, only its length, which is often the clue: a pasted secret with an extra character won't match.

From there R2 behaves like any other site: dual-pane browsing, drag and drop from Finder, editing a file in place and folder sync. More on the feature set is on the features page.

Things that behave differently from a server

R2, like S3, is object storage, not a filesystem. A few differences show through in any client:

Keep an eye on operation counts

R2 bills by storage and by operations, and listing a bucket counts as an operation. A one-off browse costs next to nothing, but anything that polls adds up. Wharf's continuous sync re-scans the remote side on a timer, one listing per folder, every 15 seconds by default. For a large tree in R2, raise the interval under Settings › Sync ("Check the server every…"); the setting's own help text points out that shorter intervals mean more billable requests on object storage. Cloudflare's R2 documentation lists which calls fall into which operation class.

If you also use DigitalOcean Spaces or a self-hosted MinIO, the setup is almost the same; the Spaces and MinIO guide covers what changes. For questions about the app itself, the support page has answers to the common ones.