Back to blog
· Benoit Aubert

How to Offload WordPress Media to Cloudflare R2 (Free)

Step-by-step guide to offloading your WordPress media library to Cloudflare R2 using StaticQ Media — with automatic resizing, WebP conversion, and CDN delivery.

cloudflare r2wordpressmedia offloadtutorial

Your WordPress media library is eating your server’s disk space, slowing down backups, and costing you bandwidth every time a visitor loads an image. Meanwhile, Cloudflare R2 offers 10 GB of free object storage with zero egress fees — but connecting it to WordPress has always required custom code or expensive plugins.

Not anymore. In this guide, you’ll set up a complete media pipeline that automatically resizes images, converts them to WebP, offloads them to your own R2 bucket, and serves them through Cloudflare’s CDN — all for free.

Video: How to Offload WordPress Media to Cloudflare R2

Coming soon — 3 min walkthrough

Prefer to watch? The video above walks through every step below.


What You’ll Need

  • A WordPress site (self-hosted, any hosting provider)
  • A free Cloudflare account with your domain proxied through Cloudflare
  • About 10 minutes

That’s it. No paid plans, no API subscriptions, no per-image fees.

Step 1: Create a Cloudflare R2 Bucket

Log into your Cloudflare dashboard and navigate to R2 Object Storage in the left sidebar.

Click Create bucket. Give it a descriptive name — something like yoursite-media. Choose the region closest to your audience (or leave it on automatic).

📷

Screenshot: Cloudflare R2 bucket creation

Your bucket is ready. R2’s free tier includes 10 GB of storage and 10 million read requests per month — more than enough for most WordPress sites. Beyond that, it’s $0.015/GB with zero egress fees. You’ll never pay for the bandwidth visitors use to load your images.

Step 2: Generate R2 API Credentials

Still in the Cloudflare R2 section, click Manage R2 API Tokens (or navigate to your R2 overview and find the API tokens link).

Click Create API token. Give it a name like “StaticQ WordPress” and set the permissions to Object Read & Write. You can scope it to your specific bucket for extra security.

After creating the token, you’ll see:

  • Access Key ID
  • Secret Access Key

Copy both — you’ll need them in a moment. The Secret Access Key is only shown once.

📷

Screenshot: R2 API token credentials

Step 3: Install StaticQ Media

In your WordPress admin, go to Plugins > Add New and search for “StaticQ Media”. Click Install and then Activate.

Alternatively, download it from WordPress.org and upload the zip.

📷

Screenshot: WordPress plugin install

Step 4: Connect R2 to StaticQ

Go to StaticQ > Settings. You’ll see the storage provider is already set to Cloudflare R2.

Enter your credentials:

  • Account ID — found in your Cloudflare dashboard URL or the R2 overview sidebar
  • Access Key ID — from Step 2
  • Secret Access Key — from Step 2
  • Bucket Name — the name you chose in Step 1

For better security, you can add these as constants in your wp-config.php instead of the settings page:

define('STATICQ_R2_ACCOUNT_ID', 'your-account-id');
define('STATICQ_R2_ACCESS_KEY_ID', 'your-access-key-id');
define('STATICQ_R2_SECRET_ACCESS_KEY', 'your-secret-access-key');
define('STATICQ_R2_BUCKET', 'yoursite-media');

Click Test Connection. You should see a green checkmark confirming StaticQ can read and write to your bucket.

📷

Screenshot: StaticQ R2 settings with connection test

Step 5: Configure Processing Settings

While you’re in Settings, review the processing options:

  • Storage Mode — choose “Cloud Only” to free up local disk space, or “Local + Cloud” to keep a local backup
  • WebP Conversion — enable this to automatically generate WebP variants for every image
  • Image Resizing — choose between local processing (your server’s GD/Imagick) or Cloudflare Image Resizing (offloads CPU work to Cloudflare’s edge)
  • Front-End Delivery — enable <picture> tag delivery so visitors automatically get WebP when their browser supports it
📷

Screenshot: Processing settings — WebP and delivery enabled

Step 6: Register Your Existing Media Library

New uploads are handled automatically from this point forward. But your existing images need to be indexed first.

Go to StaticQ > Media Manager and click Register Unregistered Attachments. This scans your WordPress media library and creates tracking records for each attachment. It’s a read-only operation — nothing is modified.

📷

Screenshot: Registration progress bar

Once registration completes, StaticQ knows about your existing images. New uploads will be processed automatically from now on. For your existing library, run the Media Library Scanner (see the optional step below) to detect what’s missing — then Fix All to generate thumbnails, WebP variants, and upload everything to R2.

Step 7: Verify It’s Working

After the queue has processed a few images, check three things:

1. R2 bucket has files: Go to your Cloudflare R2 bucket and browse the contents. You should see your images organized in the same folder structure as your WordPress uploads directory.

📷

Screenshot: R2 bucket with uploaded images

2. Front-end serves from R2: Visit a page on your site, right-click an image, and inspect it. You should see a <picture> element with <source> tags pointing to your R2 domain, including WebP variants.

📷

Screenshot: DevTools showing picture tag with R2 URLs

3. Media Manager shows status: Back in the Media Manager, the registration badges should show your images as registered.

What Happens Next

From this point, everything is automatic:

  • New uploads enter the queue immediately — resized, converted to WebP, and offloaded to R2
  • Front-end delivery rewrites image tags to serve from R2 with WebP fallback
  • WordPress cron processes the queue in controlled batches — no CPU spikes, even on shared hosting

You can fine-tune the processing speed in wp-config.php:

// Process 3 images per cron tick (good default for most hosts)
define('SQ_CRON_BATCH_SIZE', 3);

// Allow 25 seconds per tick
define('SQ_CRON_MAX_SECONDS', 25);

On a dedicated server or VPS, you can push SQ_CRON_BATCH_SIZE to 5-10 for faster throughput.

Optional: Standardize Your Existing Library

This step is not required. Your existing images will continue to work as they always have — StaticQ’s front-end delivery will serve them with CDN URLs and WebP sources regardless. But if you want your entire library to match your current settings — uniform thumbnail sizes, WebP variants for every image, everything offloaded to R2 — the Media Library Scanner can get you there.

Go to StaticQ > Media Manager and click Scan Media Library. The scanner compares every attachment against your current settings — missing sizes, missing WebP variants, files not yet uploaded to R2. If it finds discrepancies, click Fix All to bring everything in line. This is especially useful if you’ve changed themes, added new image sizes, or enabled WebP after your library was already built.

📷

Screenshot: Media Library Scanner results

What You’ve Built

In about 10 minutes, you’ve set up a complete media pipeline:

  • Images are resized to all your theme’s registered sizes
  • WebP variants are generated automatically
  • Everything is offloaded to Cloudflare R2 — your server’s disk is freed up
  • Visitors get CDN-delivered images with automatic WebP fallback
  • The whole thing runs on WordPress cron — no CPU spikes, no timeouts
  • Total cost: $0 (within R2’s generous free tier)

No monthly subscriptions. No per-image fees. No third-party CDN lock-in. Your images, your bucket, your infrastructure.

Get StaticQ Media →