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:
- The endpoint. Amazon's hostnames look like
s3.eu-west-2.amazonaws.com. R2's S3 endpoint is per account:<ACCOUNT_ID>.r2.cloudflarestorage.com. Buckets created with a jurisdiction restriction (such as the EU) use a jurisdiction-specific endpoint, which the dashboard shows alongside the bucket. - The region. The region is part of every signature. R2 doesn't ask you to pick a region the way S3 does, and it expects
auto. - The credentials. R2 doesn't accept your Cloudflare login or a global API key over the S3 API. It wants an access key ID and secret access key generated from an R2 API token.
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:
- Object Read & Write is enough for browsing and transferring files. Admin permissions also let the token create and delete buckets, which a file client doesn't need.
- Scope it to specific buckets where you can. A token that can reach one bucket can't damage the others if it leaks.
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
| Symptom | Usual cause |
|---|---|
| Signature does not match | The secret was pasted with a trailing space or newline, or the region isn't auto. |
| Access denied | The token is read-only, or scoped to a different bucket. |
| Hostname or certificate error | The bucket ended up in the hostname, or the endpoint includes the bucket name as well. |
| Bucket not found | The 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:
- Protocol: Amazon S3.
- Bucket: just the bucket name. Wharf strips a pasted
s3://prefix or trailing slash, and if the name still can't form a valid URL it says which part is wrong rather than failing with a generic error. - Region: the picker includes Cloudflare R2 (auto). Choose it.
- Endpoint:
<ACCOUNT_ID>.r2.cloudflarestorage.com. A leadinghttps://is removed for you. - Put bucket in the path: turn this on for path-style requests.
- Access key ID and Secret access key: from the R2 token. The secret is stored in your Mac's Keychain, never in a configuration file.
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:
- Folders are prefixes. A "folder" is a shared prefix in key names, or a zero-byte placeholder object ending in a slash. Wharf creates folders using that placeholder convention.
- There is no rename. Renaming copies the object server-side and deletes the original. It's quick, because the data doesn't pass through your Mac, but it's two operations.
- There are no Unix permissions. Access is controlled by tokens and bucket settings, so Wharf's permissions editor reports the operation as unsupported rather than pretending to apply it.
- Content types matter if you serve files publicly. If the bucket is exposed through a custom domain, browsers rely on the stored content type. Wharf sets it from the file extension on upload (HTML, CSS, JavaScript, JSON, common image formats, fonts and so on), so a stylesheet arrives as
text/cssrather than a generic binary type. - Large uploads go in one request. Wharf uploads each file with a single PUT rather than a multipart upload. That suits website assets, backups of ordinary size and build artefacts. For very large single objects, a multipart-capable tool such as the AWS CLI is the better fit; S3 caps a single PUT at 5 GB, and R2 has a similar ceiling.
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.