What offloading the media library changes
Offloading keeps every attachment row in the WordPress database and moves the file behind it into an S3 or Cloudflare R2 bucket, which a CDN then serves from its own hostname. The library still lists the image, the post still uses the attachment ID, and only the URL that reaches the browser changes.
- wp-content/uploads stops growing on the hosting account.
- Attachment URLs render from the CDN host, not from your domain.
- The origin keeps serving HTML, PHP and MySQL.
- Disk savings arrive only when local copies are deleted.
A 2 GB uploads folder on a shared plan is the usual trigger, because every hosting backup and every migration copies it again. Offloading cuts that weight without touching the theme or the editor.
S3 or Cloudflare R2: which bucket to use
Choose R2 when traffic is unpredictable and the bill must stay flat, because R2 does not charge for egress. Choose S3 when the AWS account, IAM policies and CloudFront already exist and the team knows that lane.
| Bucket | How it bills | Fits |
|---|---|---|
| Amazon S3 + CloudFront | Storage, requests, and every GB delivered | AWS shops, lifecycle rules, multiple regions |
| Cloudflare R2 | 10 GB free, then $0.015/GB-month, no egress fee (Cloudflare figures read 29 Oct 2026) | Image-heavy Indian sites with spiky traffic |
| DigitalOcean Spaces | Flat monthly space with a bandwidth allowance | One-region portfolios and brochure sites |
| Bunny Storage | Per-GB storage plus per-GB delivery | Teams already on Bunny CDN |
Put the bucket in the region closest to the people who read the site. Mumbai (ap-south-1) for an Indian audience; a European bucket adds a round trip to every first view, whatever the CDN does later.
Connect WP Offload Media to the bucket
The plugin cannot move anything until the key, the bucket and the CDN hostname are saved. Work in this order and test one upload before the bulk run.
- Export the database and download wp-content/uploads:
wp db export pre-offload.sqlplus an FTP copy. Both are needed if the switch goes wrong. - Create the bucket. On S3, pick ap-south-1 and keep Block Public Access on; on R2, add a custom domain so files have a stable hostname.
- Create a key scoped to that one bucket: an IAM user with PutObject, GetObject and DeleteObject on arn:aws:s3:::your-bucket/*, or an R2 API token with Object Read & Write.
- Install the plugin with
wp plugin install amazon-s3-and-cloudfront --activatefor the Lite build, or upload the Pro zip under Plugins, Add New, Upload Plugin. - Open Settings, then WP Offload Media. Choose the provider, paste the key and secret, set the bucket and region, set Path to wp-content/uploads, and switch Force HTTPS on.
- Paste the CDN hostname (the CloudFront distribution or the R2 custom domain) into the CDN field and save.
Lite covers Amazon S3 and DigitalOcean Spaces. Cloudflare R2, Google Cloud Storage and the CSS, JS and font offload need the Pro licence; Media Cloud is a free plugin that speaks R2 if you want a trial run.
Rewrite the URLs already in the database
Saving the settings only affects new uploads. Old files keep local URLs until the bulk tool touches them: open Media, then Library, switch to list view, select all, then Bulk actions, Offload. The counter under Settings shows how many attachments still wait.
Large libraries time out in the browser. Run the action on 200 files at a time, or use a plugin that ships a WP-CLI command, for example wp cfr2 offload all --batch-size=100 with a --dry-run pass first.
What the bulk step changes is attachment metadata, not your prose. WordPress asks the plugin for the attachment URL at render time, so the database can still hold /wp-content/uploads/2026/10/photo.jpg while the page shows the CDN hostname. A URL typed into a paragraph, a widget or a page-builder background is not an attachment URL and stays on the old host.
Find those before launch with wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%wp-content/uploads%' AND post_status='publish' LIMIT 30;", then replace the host with a dry run first: wp search-replace 'https://webji.. Repeat without --dry-run when the report lists only the rows you expect.
What breaks after an offload, and the fix
Nearly every failure after a switch is one of six, and each has a screen or a command that clears it. Check the count under Settings first: if it does not read Offloaded all, nothing else is worth debugging.
| Symptom | Cause | Fix |
|---|---|---|
| Images still load from your domain | The bulk offload never ran on files uploaded before the plugin | Media, Library, select all, Bulk actions, Offload |
| Grey boxes instead of thumbnails | Only the original reached the bucket, not the resized versions | Regenerate thumbnails, then offload the missing sizes |
| Mixed content warning | The CDN field was saved with http:// | Change it to https:// and purge the page cache |
| Fonts or CSS missing | The CDN response carries no Access-Control-Allow-Origin | Add that header at the CDN for font and CSS paths |
| WooCommerce gallery empty | An imported CSV stored full image URLs, not attachment IDs | Re-import the image column or run the scoped search-replace |
| Restored backup shows no images | Local files were deleted and the hosting backup excludes the bucket | Keep a bucket copy or a sync job alongside the account backup |
The WordPress image editor also fails once a local file is gone, because cropping reads the file from disk. Leave Remove Local Files off for the first week and delete in one pass after the bucket copy is confirmed.
Verify that the CDN is serving the files
Take a URL from the media library, not from the page source, and ask the CDN directly.
curl -sI https://cdn.
- 200 with content-type image/jpeg means the object is in the bucket.
- Cloudflare adds cf-cache-status: HIT on the second request; CloudFront shows x-cache: Hit from cloudfront.
- cache-control public with a long max-age means the edge will keep it.
Then load the post, view source, and count img tags that start with the CDN host against those that start with your domain. The second count should be zero, apart from images you deliberately kept local. Upload one new image afterwards and confirm its URL already points at the CDN.
Rollback, backups, and where hosting fits
Rollback is a plugin action, not a restore. While local copies exist, use Restore in the media library, or wp cfr2 restore all with the R2 plugin, then deactivate. URLs return to /wp-content/uploads. After local files are deleted the bucket is the only copy, so export it before cancelling storage.
Media offload does not shrink the database, and a database backup still decides whether a restore works. Our WordPress website backup covers files and MySQL on the account, which is why the first week should keep both copies.
The hosting account still runs PHP, MySQL and the page cache. WebJi® WordPress hosting starts at ₹400/month before 18% GST on the WordPress Unlimited plan, includes free Let's Encrypt SSL, and the desk will review an offload setup during a migration. Sites that already serve images from a CDN get the most from the WordPress speed guide, which covers what to fix next.
Frequently Asked Questions
Do I need WP Offload Media Pro for Cloudflare R2?
Yes. The Lite build on WordPress.org connects Amazon S3 and DigitalOcean Spaces only. Cloudflare R2, Google Cloud Storage and the CSS, JS and font offload are sold in the Pro licence, and Media Cloud is a free plugin with R2 support if you want to test first.
Will offloading break existing images?
No, if the bulk offload runs and local copies stay in place until you check. Attachment URLs switch at render time; images pasted into posts, widgets or builder backgrounds as full URLs keep the old host and need a search-replace.
What happens if I deactivate the plugin?
WordPress falls back to /wp-content/uploads. If those files still exist, images load as before; if Remove Local Files already deleted them, every image 404s until you reactivate the plugin or restore the bucket.
How do I check that a file is served from the CDN?
Run curl -sI against the file URL from the media library. A 200 with the right content-type plus cf-cache-status: HIT or x-cache: Hit from cloudfront means the edge has it. Then view source and confirm img tags use the CDN host.
Does offloading free disk space straight away?
No. The database still holds every attachment and the files stay on disk until Remove Local Files runs. The space appears after that pass, and hosting backups shrink on the next run.
Can I keep local copies as a backup?
Yes, WP Offload Media and the R2 plugin both leave local files in place by default. That doubles storage but keeps the rollback cheap, and it is the safer setting for the first month.

