R2 ships an S3 API and no desktop client
Cloudflare R2 is object storage that speaks the S3 API, so the SDK you already use keeps working after you change one endpoint. The reason teams move data there is the pricing page, checked September 2026: standard storage at $0.015 per GB-month, and on egress a sentence with no hedging in it — "There are no charges for egress bandwidth for any storage class."
Free egress changes what lands in a bucket: release binaries, video masters, training datasets, nightly database dumps. Then somebody needs one of those files in a normal application, and the tooling runs out.
Cloudflare's own list of R2 integrations, also checked in September 2026, documents rclone, two Terraform providers and fourteen S3 SDK pages — aws CLI, boto3, aws-sdk-js-v3, aws4fetch, s3mini and the rest. There is no GUI client of any kind in that list. The first-party options are the dashboard, wrangler (Cloudflare documents a 315 MB per-file ceiling for uploads through it), or the AWS CLI with an --endpoint-url flag. AnyStorage is the missing layer: a desktop workspace that treats an R2 bucket like a folder tree.
The four fields that connect R2
An R2 connection is an S3 connection with a specific endpoint. The S3 API docs give the hostname form, and the API token page documents the jurisdiction variants:
sh
# Standard account endpoint
https://<ACCOUNT_ID>.r2.cloudflarestorage.com
# Buckets created with a jurisdiction need the matching endpoint
https://<ACCOUNT_ID>.eu.r2.cloudflarestorage.com
https://<ACCOUNT_ID>.fedramp.r2.cloudflarestorage.com
https://<ACCOUNT_ID>.us.r2.cloudflarestorage.comPoint a client at the plain account endpoint when the bucket was created in the EU or FedRAMP jurisdiction and the bucket is simply not there — the most common "my keys must be wrong" report that is not about keys.
The token. The dashboard path is R2 object storage, then Account Details, then Manage API Tokens. The documented tiers are Admin Read & Write, Admin Read only, Object Read & Write and Object Read only. A desktop client wants Object Read & Write — "read, write, and list objects in specific buckets" — and the same page confirms object-level tokens can be scoped "to a set of buckets" rather than the whole account. The secret access key is shown once.
The region. The R2 documentation states that "the region for an R2 bucket is auto", and that "an empty value and us-east-1 will alias to the auto region". Anything else is a guess.
Addressing style. R2 accepts both. Path-based routing came first; Cloudflare's release notes record "support for S3 virtual-hosted style paths, such as <BUCKET>.<ACCOUNT_ID>.r2.cloudflarestorage.com" landing on 16 May 2022. Leave path-style off and R2 is fine, turn it on and R2 is still fine — the opposite of a self-hosted MinIO server, where path-style is the only mode until you configure a domain.
In AnyStorage that is one dialog: add a connection, choose Cloudflare R2, paste the endpoint and the key pair. Credentials stay encrypted on the machine, and there is no account to create.
Where R2 stops behaving exactly like S3
Cloudflare publishes an S3 API compatibility matrix, checked here in September 2026:
- ACLs do not exist:
x-amz-acland the wholex-amz-grant-*family are unimplemented; access comes from tokens, not per-object grants. - Object tagging is unimplemented, and lifecycle features are listed as "currently in progress".
- Storage classes are limited to
STANDARDandSTANDARD_IA. - Server-side encryption covers SSE-C, not the AWS KMS key header; checksums cover composite CRC-32, CRC-32C, SHA-1 and SHA-256, but not
FULL_OBJECT.
The size limits matter more day to day, because they decide whether a transfer succeeds.
| Limit | Value |
|---|---|
| Maximum object size | 5 TiB (4.995 TiB exactly) |
| Maximum single-part upload | 5 GiB (4.995 GiB exactly) |
| Minimum multipart part size | 5 MiB, except the last part |
| Part sizes | All parts except the last must be the same size |
| Maximum parts per upload | 10,000 |
| Object key length | 1,024 bytes |
| Buckets per account | 1,000,000 |
Read the first two rows together: anything over about 5 GiB has to go up as a multipart upload, so a tool that only knows how to PUT a whole file fails on a large video and says very little about why.
Presigned URLs, with three edges
Yes, R2 supports presigned URLs, and the documentation is specific: AWS Signature Version 4, an expiry range of "1 second to 7 days (604,800 seconds)", and four methods — GET, HEAD, PUT and DELETE. Two limits are easy to trip over. POST, meaning multipart form uploads from HTML forms, "is not currently supported". And presigned URLs "cannot be used with custom domains"; they work only against the S3 API domain.
AnyStorage generates presigned upload and download links on Pro — the fastest way to hand one object to somebody who should not get a token. The presigned URL notes cover the S3 side.
Public buckets and r2.dev
R2 can expose a bucket on a Cloudflare-managed r2.dev subdomain, and the documentation is blunt about what that is for: access through r2.dev "is rate-limited and should only be used for development purposes", and the endpoint "is intended for non-production traffic". For WAF custom rules, caching, access controls or Bot Management, the docs say the bucket must sit behind a custom domain.
So r2.dev is not a distribution plan. One-off sharing is a presigned URL; real delivery is a custom domain.
When CORS is the thing that is broken
A desktop client never sends a browser preflight, which makes it a diagnostic: if AnyStorage lists and downloads the bucket but your web app cannot, the credentials are fine and the CORS policy is the suspect.
Cloudflare's R2 CORS documentation, checked September 2026, defines AllowedOrigins, AllowedMethods, AllowedHeaders, ExposeHeaders and MaxAgeSeconds. Origins "must match exactly" — scheme, host and port, so http://localhost:3000 is not https://localhost:3000. Custom headers are opt-in: requests carrying a header such as x-user-id "should specify these headers as AllowedHeaders", and the troubleshooting step is to "make sure the rule's AllowedHeaders includes the blocked request's headers". Nothing on that page promises that * means "any header", so name each one. MaxAgeSeconds is the other trap: browsers cache the preflight, so a corrected policy looks broken until that cache expires. The R2 CORS error guide works through the specific messages.
How the R2 tools compare
| Tool | GUI | Mounts as a drive | Listed in Cloudflare's R2 docs | Linux | Price |
|---|---|---|---|---|---|
| AnyStorage | Yes, desktop app | Yes, local WebDAV server, no driver | No | Yes, AppImage and .deb | Free tier, paid Pro |
| rclone | No, command line | Yes, needs FUSE or WinFsp | Yes | Yes | Free, open source |
| Cyberduck | Yes, desktop app | No | No | No | Free, open source |
| AWS CLI | No, command line | No | Yes, as the aws CLI page | Yes | Free |
| Cloudflare dashboard | Yes, in a browser | No | First-party | Browser only | Free |
The trade-offs behind the table:
- rclone is the most capable option here and the least convenient: the one third-party tool Cloudflare documents for R2, where
rclone mountgives real drive behaviour at the cost of a FUSE or WinFsp install plus a--vfs-cache-modedecision before many applications will write to the mount. - Cyberduck is a good free bucket browser with R2 connection profiles. It does not mount, and there is no Linux build.
- The AWS CLI is right for a copy you can describe in advance, wrong for finding a file whose name you half remember three prefixes deep. The dashboard is the right place to create buckets, tokens and custom domains, and a slow place to move files.
- AnyStorage sits between the browser and the mounter, and it is not in Cloudflare's docs — that row is honest on purpose.
Mounting R2 as a drive
Some programs accept nothing but a path. AnyStorage talks to R2 over the ordinary S3 API and re-serves every connected storage through a WebDAV server on your own machine at http://127.0.0.1:3211. No macFUSE, no WinFsp, no kernel module, no admin rights: you mount it with the WebDAV client your operating system already ships — Finder's Connect to Server, Explorer's Map network drive, dav:// in Nautilus or Dolphin. The full walkthrough is here. That server is read-only on the free tier and read-write on Pro.
Install and platforms
Version 0.2.25 went out on 23 September 2026 for macOS 11 and later, Windows 10 and later, and Ubuntu 20.04 and later; on a Mac, brew install --cask kilerd/tap/anystorage. The free tier covers two storage connections. If R2 is one bucket among several, start from the S3 GUI client overview, or the platform pages for Linux and macOS.
Questions
Does R2 support presigned URLs?
Yes. Cloudflare documents SigV4 presigned URLs for GET, HEAD, PUT and DELETE, expiring between 1 second and 7 days (604,800 seconds). POST form uploads are not supported, and a presigned URL works only against the S3 API endpoint, not a custom domain. AnyStorage generates them on Pro.
Why does my R2 CORS config fail?
Usually one of three things: the origin does not match exactly, scheme and port included; a custom request header is missing from AllowedHeaders; or the browser is still caching an old preflight for MaxAgeSeconds. The docs never state that * covers arbitrary headers, so list them. See the R2 CORS error guide.
What endpoint and region should I use for R2?
The endpoint is https://<ACCOUNT_ID>.r2.cloudflarestorage.com, or the eu, fedramp or us variant if the bucket was created with that jurisdiction. The region is auto; the docs note that an empty value and us-east-1 both alias to it.
Do I need path-style addressing for R2?
No. R2 has supported virtual-hosted style paths since May 2022 and still accepts path-based routing, so either setting works. Path-style is what a self-hosted MinIO endpoint needs, not R2.
Can I mount an R2 bucket as a drive without FUSE?
Yes. AnyStorage runs a local WebDAV server on http://127.0.0.1:3211 and your operating system's own WebDAV client mounts it, so no macFUSE or WinFsp driver is involved. Read-only on the free tier, read-write on Pro.