CPW Media Offload for Cloudflare R2

Beschreibung

CPW Media Offload for Cloudflare R2 copies your WordPress Media Library to a Cloudflare R2 bucket and rewrites the media URLs of your site so visitors download images, videos and documents from Cloudflare instead of your web server.

The Cloudflare R2 free tier includes 10 GB of storage and, when the bucket is served through a custom domain, no egress fees. That makes it a practical way to keep a small hosting plan fast: your server stops sending megabytes of images on every page view and stops filling its disk with thumbnails.

What it does

  • Uploads new media, and every generated thumbnail size, to your R2 bucket.
  • Optionally deletes the local copy after a successful upload, so the uploads folder stops growing.
  • Rewrites attachment URLs, srcset sources, post content, widgets and blocks to the public bucket URL.
  • Bulk tool that offloads an existing library in small batches, with progress and a stop button.
  • Copy files back to the server at any time, per file or for the whole library.
  • Usage bar tuned for the 10 GB free tier, measured directly on the bucket.
  • Optional WebP and AVIF conversion on delivery, done by Cloudflare itself, so no image is ever encoded on your server.
  • Optional WebP conversion on upload, for sites that would rather store the smaller file.
  • Cloudflare R2 column and per file actions inside the Media Library, including sending a changed file again.
  • Rebuild tool that restores the offload index by checking the bucket, without uploading anything.
  • Activity log so upload or permission problems are visible without server access.
  • Files sent again get a new version in their URL, so browsers and the Cloudflare cache never keep showing the old content.
  • Optional Cloudflare cache purge, so a file deleted from the bucket stops being served by the edge right away.

WebP and AVIF without loading your server

Image conversion normally costs CPU: the server decodes the upload and re-encodes it, which is slow on shared hosting and simply unavailable where PHP was built without WebP support.

This plugin can hand that work to Cloudflare instead. Turn on Convert on delivery and your media URLs are served through Cloudflare Image Transformations, which converts each image to AVIF or WebP according to what the visitor’s browser accepts, resizes it to the width actually needed, and caches the result on the edge.

  • Nothing is encoded on your web server, at upload time or afterwards.
  • The originals on the bucket are never modified, so the change is fully reversible.
  • Images already in your library are covered too: there is nothing to reprocess and nothing is rewritten in your database.
  • Optionally, WordPress stops generating thumbnails altogether and Cloudflare produces every responsive size from the original. That is where the real saving on the 10 GB allowance comes from.

This requires a custom domain connected to your bucket, because transformations are served by the Cloudflare zone that answers for that domain. The r2.dev development address cannot do it. The Cloudflare free plan includes 5,000 unique transformations per month; repeat visitors are served from the cache and cost nothing.

Page builder and theme compatibility

Media URLs stored inside page builder data are rewritten as well, so background images, galleries and sliders keep working after an offload:

  • Elementor and Elementor Pro, including generated CSS files and background images.
  • Divi, Beaver Builder and WPBakery Page Builder.
  • Block themes, classic themes, the block editor and any theme that uses the standard WordPress media functions.

Elementor’s CSS cache is cleared automatically after an offload so its generated files point at the new URLs.

Page cache plugins

When media URLs change, because an existing library is offloaded, a file is copied back to the server or sent again, or delivery from R2 is switched on or off, the page cache is cleared so visitors never get pages that still point at the old addresses. Supported: WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, WP Fastest Cache, SiteGround Speed Optimizer, Cache Enabler, Comet Cache, Breeze, Hummingbird, WP-Optimize, Swift Performance, Autoptimize, Nginx Helper, Kinsta, WP Engine and Pantheon. During a bulk run the cache is cleared once, a couple of minutes after the changes stop, and new uploads never clear it.

Languages

The plugin is fully internationalised. Translations are provided through translate.wordpress.org, where Brazilian Portuguese and any other language can be contributed.

Security

  • The secret access key is encrypted with your WordPress security keys before it is stored.
  • Credentials can be defined as constants in wp-config.php and kept out of the database entirely.
  • Every form is protected with nonces and capability checks.
  • No third party SDK is bundled: requests are signed with AWS Signature Version 4 and sent through the WordPress HTTP API.

External services

This plugin connects to the Cloudflare R2 storage service, which is the whole purpose of the plugin, and, only if you set up the optional cache purge, to the Cloudflare API. It does not connect to any other external service and it sends no analytics or telemetry anywhere.

Cloudflare R2 (S3 compatible API)

  • When: whenever a file is offloaded, restored, deleted or listed, and when you use the Test connection or Recalculate storage usage buttons.
  • Where: https://<your-account-id>.r2.cloudflarestorage.com (or the EU or FedRAMP jurisdiction endpoint you select, or a custom endpoint you enter).
  • What is sent: the media files you choose to offload, their file names and content types, your R2 access key id, a request signature generated from your secret access key, and a user agent string containing the plugin version and your site address.
  • What is received: the storage responses of the service, including object listings and sizes used by the usage bar.

Files served to your visitors are additionally requested from the public bucket URL you configure (an r2.dev address or your own custom domain).

Cloudflare API (optional cache purge)

  • When: only when a zone ID and an API token are filled in, whenever a file is deleted from the bucket or sent to it again, and when you click Test cache purge.
  • Where: https://api.cloudflare.com/client/v4/zones/<your-zone-id>.
  • What is sent: your API token, and the public URLs of the files whose cached copy must be cleared.
  • What is received: whether the request succeeded.

Cloudflare terms of service: https://www.cloudflare.com/terms/
Cloudflare privacy policy: https://www.cloudflare.com/privacypolicy/

Screenshots

Installation

  1. Install and activate the plugin.
  2. In your Cloudflare dashboard create an R2 bucket and an R2 API token with Object Read and Write permission.
  3. Go to Media Cloudflare R2 and fill in the account id, access key id, secret access key and bucket name.
  4. Click Test connection.
  5. Give the bucket a public URL: either enable the r2.dev development URL, or, recommended for production, connect a custom domain to the bucket in Cloudflare. Paste that address in Public bucket URL.
  6. Open the Offload tools tab and run Start offloading to move the existing library.
  7. Back in Settings, enable Serve media from R2.
  8. Only after checking that your site renders correctly, enable Delete the local copy after a successful upload to free disk space.
  9. Optional, and only if step 5 used a custom domain: enable transformations for that zone in Images Transformations in your Cloudflare dashboard, then turn on Convert on delivery so Cloudflare serves WebP and AVIF without your server encoding anything.

FAQ

Do I need a paid Cloudflare plan?

No. The R2 free tier includes 10 GB of storage per month plus a monthly allowance of read and write operations, which is enough for most small and medium sites. The plugin shows how much of that allowance your media uses.

Is there a Pro version?

Yes, as a separate add-on. Everything this plugin does is free and will stay free. CPW R2 Pro adds what stores, agencies and larger sites need: private files with signed URLs for WooCommerce and Easy Digital Downloads, background offload with WP-CLI, Multisite and theme and plugin assets served from R2.

Does it work with Elementor?

Yes. Beyond the standard WordPress media functions, the plugin rewrites the URLs stored inside Elementor documents, so widgets, galleries and background images all point at Cloudflare. The Elementor editor keeps working with local paths, and Elementor’s CSS cache is cleared after an offload.

Will my media break if I deactivate the plugin?

No, as long as the local copies still exist: URLs simply go back to your server. If you enabled local deletion, run Copy everything back to the server in the Offload tools tab before deactivating.

Can I keep using the WordPress image editor?

Yes. Since version 1.5.0 it also works with local deletion enabled: when you open an image in the editor, crop it in the Customizer or pick it as the site icon, the plugin copies it back to the server first. Once you save, the edited image is offloaded again and, with local deletion on, removed from the server.

Does it delete my files from R2 when I uninstall the plugin?

Never. Uninstalling removes only the plugin settings, log and internal post meta. Your bucket is left untouched.

I deleted the plugin and lost the record of what was offloaded. What now?

Your files are still on the bucket: the plugin never deletes them. Go to the Offload tools tab and run Rebuild the index, which checks every attachment against the bucket and restores the record without uploading anything. To stop it happening again, keep the setting Keep the settings and the offload index enabled, which is the default from version 1.1.0. Note that deleting a plugin runs its clean up routine, while updating one does not.

Does the plugin convert my images to WebP?

If you turn it on, and there are two ways to do it. Convert on delivery hands the work to Cloudflare, which is the recommended option and is covered in its own question below. Convert to WebP under Offloading does it on your own server instead: JPEG and PNG uploads are converted before the thumbnails are generated, so every size is WebP and the smaller file is what counts against your storage. GIFs are never converted so animations survive, and an image is only replaced when the WebP version really is smaller. It applies to new uploads: images already in the library are left as they are.

Can Cloudflare convert my images instead of my server?

Yes, and it is the better option. Turn on Convert on delivery under Image optimisation on Cloudflare. Your images are then served through Cloudflare Image Transformations, which converts them to AVIF or WebP per browser and caches the result. Your server never decodes or encodes an image, the originals on the bucket stay as they are, and images already in your library are covered without any reprocessing.

You need a custom domain connected to your bucket and transformations enabled for that zone in Images Transformations in the Cloudflare dashboard. The r2.dev development address does not go through a zone and cannot transform anything; the settings screen tells you when that is the case.

What does the Cloudflare conversion cost?

The Cloudflare free plan includes 5,000 unique transformations per month. A unique transformation is one combination of options applied to one image: the first visitor pays for it and everyone after that is served from the cache, at no cost. Beyond the free allowance Cloudflare charges per thousand. The plugin uses a short ladder of five widths by default to keep that count low, and the cpw_r2_transform_widths filter lets you shorten it further.

Should I use the upload conversion or the delivery conversion?

Use one or the other, not both. The delivery conversion is the better default: it costs your server nothing, keeps your originals intact and applies to your whole library at once. The upload conversion is for sites without a custom domain on Cloudflare, or for anyone who would rather store the smaller file on the bucket and accept losing the original.

Can several sites share one bucket?

Yes. Give each site a different Path prefix, for example site-a and site-b.

I replaced a file but visitors still see the old one. Why?

Since version 1.5.0 this is handled for you. Every time a file is sent to the bucket again, its URL gets a new ver value, so browsers and the Cloudflare cache fetch the new content instead of the copy they kept for a year. Files offloaded only once keep their plain URL.

Do deleted files keep being served by Cloudflare?

On a custom domain, yes, until their cache expires: deleting an object from the bucket does not touch the copy Cloudflare keeps on the edge. To clear it automatically, fill in Zone ID and API token under Delivery > Cloudflare cache purge. The token needs the Zone > Cache Purge permission only. The plugin then clears the cached copy whenever a file is deleted from the bucket or sent again, and Test cache purge checks the values without purging anything. If Cloudflare times out, limits the requests or has an outage, the files are queued and tried again automatically, waiting longer each time, for about half an hour; a refused token is reported in the Log tab instead of retried. Both values can also be defined in wp-config.php as CPW_R2_CF_ZONE_ID and CPW_R2_CF_API_TOKEN.

Some images have no thumbnails on the bucket. How do I fix them?

Versions before 1.5.0 offloaded an upload before WordPress had created its thumbnails, so only the main file reached the bucket and, with local deletion on, no thumbnail was ever made. Run Send everything again once in the Offload tools tab. It brings back the files whose local copy was deleted, creates the missing thumbnails and sends every size to the bucket.

What happens to files that fail to upload?

They are flagged and skipped so the bulk tool can continue. The reason is recorded in the Log tab, and Queue failed files again puts them back in line.

Rezensionen

5. September 2026
I tried several plugins before this one to offload my media library to R2. Some were limited, while others had bugs that prevented a complete sync. This plugin really has everything you need, including a valuable feature to keep track of Cloudflare's free tier limits. Best of all, it organizes its settings in a clear interface that's super easy to configure. Great job!
1. September 2026
I installed this plugin to reduce my hosting storage costs, and the results are fantastic. It automatically moves the entire Media Library to Cloudflare R2 and successfully frees up local server disk space. The integration with Elementor and my current theme worked flawlessly without breaking any images or layouts. Serving the images through Cloudflare's CDN has also made the website noticeably faster. It delivers exactly what it promises, features an easy setup, and solves storage issues efficiently. Highly recommended for anyone looking to offload their media!
Alle 2 Rezensionen lesen

Mitwirkende und Entwickler

„CPW Media Offload for Cloudflare R2“ ist Open-Source-Software. Folgende Menschen haben an diesem Plugin mitgewirkt:

Mitwirkende

„CPW Media Offload for Cloudflare R2“ wurde in 1 Sprache übersetzt. Danke an die Übersetzer für ihre Mitwirkung.

Übersetze „CPW Media Offload for Cloudflare R2“ in deine Sprache.

Interessiert an der Entwicklung?

Durchstöbere den Code, sieh dir das SVN-Repository an oder abonniere das Entwicklungsprotokoll per RSS.

Änderungsprotokoll

1.5.2

  • New: a Cloudflare cache purge that fails for a passing reason (timeout, rate limit, Cloudflare outage) is no longer lost. The files are queued and tried again in the background, waiting longer each time and following Cloudflare’s Retry-After, for about half an hour. Before, a failure also skipped every batch after the one that failed.
  • Clearer messages when Cloudflare refuses the API token or limits the requests. A refused token is not retried.

1.5.1

  • New: the page cache is cleared when media URLs change, so cached pages stop pointing at local files that were deleted after the offload. Works with WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, WP Fastest Cache, SiteGround Speed Optimizer, Cache Enabler, Comet Cache, Breeze, Hummingbird, WP-Optimize, Swift Performance, Autoptimize, Nginx Helper, Kinsta, WP Engine and Pantheon. It runs once a couple of minutes after a bulk run settles (at most ten minutes after it starts), right away when the settings are saved, and never for new uploads.
  • For developers: the cpw_r2_flush_page_cache filter turns it off, and the cpw_r2_page_cache_flushed action receives the list of caches cleared, to plug in any other cache.

1.5.0

  • New: a file sent to the bucket again gets a new version in its URL (?ver=), in attachment URLs, responsive images and page content. With the one year Cache-Control the old content used to stay in browsers and on the Cloudflare edge. Files offloaded only once keep the URL they have.
  • New: optional Cloudflare cache purge. With a zone ID and an API token with the Cache Purge permission, the cached copy is cleared whenever a file is deleted from the bucket or sent again. This also covers files for sale taken off the public bucket, which the edge could otherwise keep serving.
  • New: Test cache purge button, and the CPW_R2_CF_ZONE_ID and CPW_R2_CF_API_TOKEN constants for wp-config.php.
  • External services: the cache purge talks to the Cloudflare API (api.cloudflare.com) only when both values are filled in.
  • Fix: on upload, the media was offloaded on the first metadata save WordPress makes, before any thumbnail existed. Only the main file reached the bucket and, with local deletion on, it was deleted before WordPress could create the thumbnails, so none were made. The offload now waits until every size is ready. To repair images uploaded with an earlier version, run Send everything again once in the Offload tools tab.
  • New: the WordPress image editor, the Customizer crop and the site icon work with local deletion enabled. The image is copied back to the server when it is opened for editing, so there is no need to use Copy back to server first.
  • New: Send everything again in the Offload tools tab sends every offloaded file again with all its sizes. Files whose local copy was deleted come back from the bucket first and their missing thumbnails are created.
  • For developers: CPW_R2_Media::versioned_url(), the cpw_r2_purge_urls filter, and the cpw_r2_removed action now passes the removed payload as a second argument.

1.4.2

  • The plugin screen presents the CPW R2 Pro add-on again, now that it is available. The link only opens a page on criandoparaweb.com.br: nothing is sent from your site, and nothing in the free plugin is locked or limited.

1.4.1

  • Connection fields can now be managed for a whole Multisite network: when they are, every site shows them read only. The jurisdiction, custom endpoint and path prefix fields also respect this, and so do constants in wp-config.php.
  • The plugin screen no longer presents a Pro add-on for now.
  • For developers: the cpw_r2_setting_locked filter marks any setting as managed elsewhere.

1.4.0

  • Security: files sold with WooCommerce (woocommerce_uploads) or Easy Digital Downloads (edd) are no longer sent to the public bucket, where anyone with the address could download them. If an earlier version already sent some, the plugin screen and the plugins list show a warning with a button that brings them back to your server and removes them from the bucket.
  • The plugin screen presents the CPW R2 Pro add-on, with a link to its page in English, Portuguese or Spanish, following the language of your dashboard. The link only opens a page on criandoparaweb.com.br: nothing is sent from your site, and nothing in the free plugin is locked or limited.
  • The plugin screen has a new look: a dashboard with shortcuts and library numbers, settings split into cards with switches, and a setup wizard that tests the connection to your bucket before saving anything.
  • The donation links were removed.
  • For developers: the R2 client can create time limited signed download URLs (CPW_R2_Client::presigned_url()), add-ons can register their own tabs on the plugin screen (cpw_r2_admin_tabs and cpw_r2_render_tab_{$slug}), and the Pro panel can be hidden with cpw_r2_show_pro_panel.

1.3.2

  • The donation link is easier to find: a „Buy me a coffee“ button next to the plugin title, a redesigned support panel at the bottom of the screen and a short support note in the admin footer. Nothing changed in how media is offloaded or served.

1.3.1

  • The plugin screen now has a support panel with a donation link and a shortcut to leave a review. Nothing changed in how media is offloaded or served.
  • The plugins list shows a donation link in the plugin row.

1.3.0

  • New: images can be converted to WebP or AVIF by Cloudflare on delivery, through Cloudflare Image Transformations. Your server no longer encodes anything, the originals on the bucket are left untouched, and images already in the library are covered without reprocessing.
  • New: optional output format, delivery quality and maximum delivered width for the Cloudflare conversion.
  • New: optional setting that stops WordPress generating thumbnails and lets Cloudflare produce every responsive size from the original, which is what frees space on the bucket.
  • The settings screen warns when the public bucket URL is an r2.dev address, which cannot serve transformations.

1.2.1

  • Removed the Plugin URI header, which pointed at a page that does not exist yet.
  • Translations are no longer bundled: they come from translate.wordpress.org.

1.2.0

  • The plugin is now called CPW Media Offload for Cloudflare R2. Only the name, the slug and the text domain changed: your settings, your offload index and your files on the bucket are untouched.

1.1.1

  • Every credential field on the settings screen now has a „Where to find this in Cloudflare“ panel showing the exact click path in the Cloudflare dashboard, including a warning that the secret access key is only ever displayed once.

1.1.0

  • Deleting the plugin no longer erases your settings and offload index by default. A new setting controls it, and files on the bucket were never touched to begin with.
  • New tool that rebuilds the offload index by checking the bucket, for libraries whose index was lost.
  • New „Send to R2 again“ action, per file and in bulk, for files that changed after being offloaded.
  • Optional WebP conversion of JPEG and PNG uploads, with a quality setting. Conversion runs before the thumbnails are generated, GIFs are left alone, and the original is only replaced when the WebP version is actually smaller.

1.0.1

  • Translations are now loaded without load_plugin_textdomain(), which is discouraged since WordPress 4.6. Language packs from translate.wordpress.org keep taking precedence over the bundled files.
  • The attachment lookup query now passes its meta key as a prepared placeholder.
  • Declared compatibility with WordPress 7.1.

1.0.0

  • First public release.
  • Offload of new uploads and of existing libraries to Cloudflare R2.
  • URL rewriting for attachments, srcset, post content and page builder data.
  • Elementor, Divi, Beaver Builder and WPBakery compatibility.
  • Multipart uploads for large files, bulk tools, usage meter and activity log.
  • Ready for translation through translate.wordpress.org.