How to copy files between AWS S3 and Cloudflare R2
TL;DR
S3's CopyObject requires the source and destination to share an endpoint, so a cross-provider copy has always meant reading from one and writing to the other. S3 Viewer now does that loop for you: add both as servers, then use “Copy to another bucket…” on a file, a folder, or a selection — it copies server-side when the destination is on the same server, or streams the bytes through S3 Viewer's own server when the destination is a different connected server, S3 to R2 included. For a one-off file or a modest folder that's now the easiest path. For a large or repeated migration — millions of objects, a scheduled sync — rclone or the AWS CLI still do it better.
Steps
Step-by-step.
- 01
In S3 Viewer: “Copy to another bucket…”
Add both providers as servers, then open the source file or folder's ⋮ menu (or select several rows and use the selection toolbar) and choose Copy to another bucket…. Pick the destination server, its bucket, and a destination prefix, optionally check Replace existing, and run it. A folder is relisted and copied object by object with progress, and the transfer is resumable and cancellable like a folder move. - 02
What actually moves the bytes
If the destination is on the same server — a different bucket on the same AWS or R2 account, for example — S3 Viewer issues a server-sideCopyObject; nothing passes through your browser or S3 Viewer's own server. If the destination is a different connected server, including a different provider, there is no server-side call that can do that, so S3 Viewer reads the object from the source and writes it to the destination through its own server, carrying over content type, cache headers and metadata. - 03
For a large or repeated migration, use rclone instead
S3 Viewer's transfer runs one object (or one folder's objects, sequentially with light concurrency) through a single process. For moving millions of objects, or syncing on a schedule,rcloneis still the right tool — it parallelizes aggressively, resumes, and is built for exactly that.rclone configwalks you through the two remotes, or write them by hand; R2 takesprovider = Cloudflare,region = auto, and your account endpoint.# ~/.config/rclone/rclone.conf [aws] type = s3 provider = AWS region = us-east-1 access_key_id = ... secret_access_key = ... [r2] type = s3 provider = Cloudflare region = auto access_key_id = ... secret_access_key = ... endpoint = https://<account>.r2.cloudflarestorage.com - 04
Dry-run the sync
--dry-runprints exactly what would be transferred without writing anything. Worth doing every time, especially before a sync that could delete on the destination.rclone sync aws:my-bucket r2:my-bucket --dry-run - 05
Run the rclone transfer
rclone syncparallelizes, uses multipart for large objects, retries failed transfers, and resumes if you stop it. Raise--transfersif you have the bandwidth;copyinstead ofsyncif you never want anything deleted on the destination.rclone sync aws:my-bucket r2:my-bucket \ --transfers 16 --checkers 32 --progress - 06
One-off file from a terminal: the AWS CLI
If you'd rather stay in a terminal for a single object, two profiles and a pipe do it — one for AWS, one for R2 with--endpoint-url. The bytes stream through your machine, same as S3 Viewer's server-side stream does through its server.aws s3 cp s3://aws-bucket/key.zip - --profile aws | aws s3 cp - s3://r2-bucket/key.zip \ --profile r2 \ --endpoint-url https://<account>.r2.cloudflarestorage.com - 07
Verify in S3 Viewer
Switch to the destination server in the sidebar and open the destination prefix. Object counts and sizes should match the source; open a couple of files to check their content type and preview them, whichever tool did the transfer.
Under the hood
What's actually happening.
S3's native CopyObject is server-side but requires the source and destination to share an endpoint, so it cannot cross providers. Every cross-cloud copy is therefore a GetObject followed by a PutObject (or a multipart upload), and the only real question is which machine the bytes pass through. rclone runs that read-and-write loop with configurable parallelism, splits large objects into parts, retries what fails, and resumes where it stopped — still the right choice for a large or repeated migration. S3 Viewer now runs the same loop itself for a single file, folder, or selection: “Copy to another bucket…” does a server-side CopyObject when the destination is on the same server, and a streamed GetObject → PutObject/multipart upload through S3 Viewer's own server when it isn't, reusing the same streaming upload path, admission limits and idle-timeout handling regular uploads use. It is deliberately not a bulk sync tool — no scheduling, no diffing a whole bucket — just a click for the file or folder in front of you, with the same file browser giving you both sides to check before and after.
FAQ
Common questions.
How do I copy a file from AWS S3 to Cloudflare R2?
Can I do a server-side copy across providers?
Does S3 Viewer copy files between S3 and R2?
Will my object's metadata and tags survive the copy?
What about copying R2 to S3, or S3 to MinIO?
How do I migrate millions of objects from S3 to R2?
What does the transfer cost?
Use S3 Viewer for this
Skip the CLI. Try it in the browser.
S3 Viewer turns the steps above into a single click. Open source, self-hostable, free.
Use case
Why teams pick this
Related guides
More how-tos
View S3 + R2 together
One credential per provider, one sidebar, and one click to switch between them.
Upload large files
How large uploads work in S3 Viewer and from the CLI — streaming, retries, and the 5 GB single-PUT cap.
Rename an S3 file
Amazon S3 has no rename API — keys are immutable. Here's the standard copy + delete pattern, with metadata and tags preserved.