WP Manifestindependent plugin directory
manifest / media / r2-uploads

R2 Uploads releasesself-updates

The WordPress Plugin to Store Uploads on Cloudflare R2

by Carnaval Studio · github.com/carnaval-studio/r2-uploads · website

3stars
11release downloads
0forks

Install

The author publishes release zips, so WP-CLI can install straight from GitHub:

wp plugin install https://github.com/carnaval-studio/r2-uploads/releases/download/v3.0.24/r2-uploads.zip

Ships its own WordPress updater (built-in updater), so new versions show up under Dashboard → Updates.

Readme

R2 Uploads

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.

Read the full README on GitHub →

Releases

TagPublishedAssetDownloads
v3.0.24 Jun 16, 2026 r2-uploads-3.0.24.zip 2
v3.0.24 Jun 16, 2026 r2-uploads.zip 1
v3.0.23 Jun 16, 2026 r2-uploads-3.0.23.zip 0
v3.0.23 Jun 16, 2026 r2-uploads.zip 0
v3.0.22 Jun 16, 2026 r2-uploads-3.0.22.zip 1
v3.0.22 Jun 16, 2026 r2-uploads.zip 0
v3.0.21 Jun 16, 2026 r2-uploads.zip 1
v3.0.21 Jun 16, 2026 r2-uploads-3.0.21.zip 2
v3.0.20 Jun 16, 2026 r2-uploads.zip 0
v3.0.20 Jun 16, 2026 r2-uploads-3.0.20.zip 0
v3.0.19 Jun 16, 2026 r2-uploads-3.0.19.zip 0
v3.0.19 Jun 16, 2026 r2-uploads.zip 0
v3.0.18 Jun 16, 2026 r2-uploads-3.0.18.zip 1
v3.0.18 Jun 16, 2026 r2-uploads.zip 0
v3.0.17 Jun 16, 2026 r2-uploads-3.0.17.zip 1
v3.0.17 Jun 16, 2026 r2-uploads.zip 0
v3.0.16 Jun 16, 2026 r2-uploads-3.0.16.zip 0
v3.0.16 Jun 16, 2026 r2-uploads.zip 1
v3.0.14 Jun 16, 2026 r2-uploads.zip 1
v3.0.14 Jun 16, 2026 r2-uploads-3.0.14.zip 0
v3.0.13 Jun 16, 2026 r2-uploads.zip 0
v3.0.13 Jun 16, 2026 r2-uploads-3.0.13.zip 0