WP Manifestindependent plugin directory
manifest / media / r2-uploads

R2 Uploads WP Registry grade A. No security issues foundWP RegistryNo security issues foundOpen the reportA

The WordPress Plugin to Store Uploads on Cloudflare R2

★ 3stars
21release downloads
v3.0.26latest · 1mo ago

Install

Download r2-uploads.zip From the v3.0.26 release on GitHub, Sep 22, 2026

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/carnaval-studio/r2-uploads/releases/download/v3.0.26/r2-uploads.zip
Updates itself. It ships its own updater, so new versions appear under Dashboard → Updates.

WordPress plugin for storing uploads in Cloudflare R2.

This project is a fork of humanmade/S3-Uploads, adapted to be R2-first. It uses Cloudflare R2's S3-compatible API for object operations, but public delivery is designed around an R2 custom domain.

Requirements

  • PHP >= 8.3
  • WordPress >= 5.3
  • Plugin dependencies installed or bundled with the plugin
  • Cloudflare R2 bucket
  • Cloudflare R2 API token with Object Read & Write access scoped to the bucket
  • A custom domain connected to the R2 bucket for production public media URLs

Installation

  1. Download the latest release ZIP from GitHub:

    https://github.com/carnaval-studio/r2-uploads/releases/latest/download/r2-uploads.zip
  2. Install it through Plugins > Add New > Upload Plugin.

Release builds bundle the vendor/ directory, so the plugin works without a site-level Composer autoloader.

Updates

Once installed, R2 Uploads checks GitHub releases automatically. New versions appear in Dashboard > Updates, just like plugins from the WordPress.org directory. No license key is required because the repository is public.

If you hit GitHub API rate limits on shared hosting, define a token in wp-config.php:

define( 'R2_UPLOADS_GITHUB_TOKEN', 'ghp_xxxxxxxxxxxxxxxxxxxx' );

Because the repository is public, the token only needs read access to public repositories:

  • Classic token: create a token with no scopes selected. Public repository data is readable without any scopes.
  • Fine-grained token: grant Contents: Read-only on carnaval-studio/r2-uploads.

The token is used only for the GitHub Releases API; it is never sent to the download URL of the release ZIP.

From source

Install dependencies inside the plugin directory:

composer install --no-dev --optimize-autoloader

Configuration

Add the required constants to wp-config.php:

define( 'R2_UPLOADS_BUCKET', 'my-bucket' );
define( 'R2_UPLOADS_ACCOUNT_ID', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
define( 'R2_UPLOADS_KEY', 'access-key-id' );
define( 'R2_UPLOADS_SECRET', 'secret-access-key' );
define( 'R2_UPLOADS_PUBLIC_URL', 'https://media.example.com' );

R2_UPLOADS_PUBLIC_URL should be a custom domain connected to the R2 bucket in Cloudflare. Do not use r2.dev for production traffic.

Optional jurisdiction endpoint:

define( 'R2_UPLOADS_JURISDICTION', 'eu' );

Optional region override. R2 uses auto:

define( 'R2_UPLOADS_REGION', 'auto' );

Bucket Path Options

These options control object keys in the bucket and therefore public URLs.

define( 'R2_UPLOADS_BUCKET_PATH_PREFIX', 'uploads' );
define( 'R2_UPLOADS_ADD_YEAR_MONTH_TO_BUCKET_PATH', true );
define( 'R2_UPLOADS_ADD_OBJECT_VERSION_TO_BUCKET_PATH', true );

Example object key:

uploads/2026/06/1718467200/image.jpg

Example public URL:

https://media.example.com/uploads/2026/06/1718467200/image.jpg

If R2_UPLOADS_ADD_YEAR_MONTH_TO_BUCKET_PATH is not defined, WordPress' native upload subdirectory behavior is used. If WordPress month/year folders are disabled, no year/month path is added.

R2_UPLOADS_ADD_OBJECT_VERSION_TO_BUCKET_PATH stores new uploads under a timestamp folder. This helps avoid stale CDN/cache responses when media is replaced.

Cache Headers

define( 'R2_UPLOADS_HTTP_CACHE_CONTROL', 'public, max-age=31536000, immutable' );
define( 'R2_UPLOADS_HTTP_EXPIRES', gmdate( 'D, d M Y H:i:s', time() + YEAR_IN_SECONDS ) . ' GMT' );

Admin Media Library CORS Workaround

WordPress adds crossorigin="anonymous" to images in the admin media library when they are served from a different origin than the admin. If the public domain does not send Access-Control-Allow-Origin headers, those images fail to load in the media grid.

R2 Uploads automatically strips the attribute in the admin when R2_UPLOADS_PUBLIC_URL points to a different host than the admin. This lets the images load without requiring CORS headers on the public domain.

To disable this behavior (for example, when CORS is already configured on the public domain):

define( 'R2_UPLOADS_DISABLE_ADMIN_CROSSORIGIN', true );

WP-CLI

wp plugin activate r2-uploads
wp r2-uploads verify
wp r2-uploads ls [<path>]
wp r2-uploads upload-directory <from> [<to>] [--concurrency=<concurrency>] [--verbose]
wp r2-uploads cp <from> <to>
wp r2-uploads rm <path> [--regex=<regex>]
wp r2-uploads enable
wp r2-uploads disable

Private Media

Cloudflare R2 does not support S3 object ACLs. Public media should be served through an R2 public bucket custom domain.

For private media, use presigned URLs. Presigned URLs are generated against the R2 S3 API endpoint and cannot use custom domains.

Notes

  • R2 bucket object operations use the S3-compatible API endpoint: https://<ACCOUNT_ID>.r2.cloudflarestorage.com.
  • Custom public delivery should use an R2 custom domain such as https://media.example.com.
  • R2 uses strong consistency for object reads, writes, deletes and listings.
  • R2 does not support S3 ACL APIs such as PutObjectAcl or x-amz-acl on PutObject.

Releases

13 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
v3.0.26 latest
Sep 22, 2026 1mo ago
r2-uploads.zip +1 asset
8
r2-uploads.zip2
r2-uploads-3.0.26.zip6
Sep 22, 2026 1mo ago
r2-uploads.zip +1 asset
0
r2-uploads.zip0
r2-uploads-3.0.25.zip0
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
5
r2-uploads.zip3
r2-uploads-3.0.24.zip2
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
0
r2-uploads.zip0
r2-uploads-3.0.23.zip0
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
1
r2-uploads.zip0
r2-uploads-3.0.22.zip1
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
3
r2-uploads.zip1
r2-uploads-3.0.21.zip2
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
0
r2-uploads.zip0
r2-uploads-3.0.20.zip0
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
0
r2-uploads.zip0
r2-uploads-3.0.19.zip0
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
1
r2-uploads.zip0
r2-uploads-3.0.18.zip1
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
1
r2-uploads.zip0
r2-uploads-3.0.17.zip1
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
1
r2-uploads.zip1
r2-uploads-3.0.16.zip0
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
1
r2-uploads.zip1
r2-uploads-3.0.14.zip0
Jun 16, 2026 4mo ago
r2-uploads.zip +1 asset
0
r2-uploads.zip0
r2-uploads-3.0.13.zip0
A grade

Security

R2 Uploads 3.0.26 · audited by WP Registry

No security issues found.

Audited release
3.0.26
Findings
none
Worst severity
—
Content hash
48edaa52c650056605c7f969…

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.