WordPress media offloading moves your uploads — images, videos, PDFs, audio — off the origin server and onto external object storage (S3, Cloudflare R2, Backblaze B2) or a dedicated CDN. The result: your web server stops serving binary files entirely, freeing CPU/RAM for PHP execution, while media is delivered from geographically distributed edge nodes closest to each visitor.
What Is WordPress Media Offloading?
Media offloading is the practice of storing WordPress attachment files on an external object storage service and rewriting the URLs WordPress generates so that src attributes point to the storage bucket or CDN origin — not your web server. The WordPress database still holds attachment metadata; only the physical files move.
This is distinct from a CDN sitting in front of your origin (which still fetches from your server on cache miss). True offloading means your origin server never holds the file in the first place.
Why Offload Media? The Performance Case
On a shared or underpowered VPS, every image request ties up a PHP worker or nginx process that could be handling a WooCommerce checkout or a logged-in user session. Static file serving is I/O-bound work your application server is poorly suited for.
According to HTTP Archive's Web Almanac, images account for 44.5% of total page weight on the median desktop page (2024). Serving that volume from a single-region origin server is a scaling liability.
The specific gains from offloading depend on your traffic geography and file mix, but the mechanisms are well understood:
- Reduced origin load: Eliminates disk I/O and network egress from your web server for every media request
- CDN edge delivery: Requests are served from the PoP nearest to the visitor, reducing latency by 50–200ms depending on geography
- Horizontal scale: Object storage like S3 scales infinitely without server provisioning
- Cheaper storage: S3 Standard charges ~$0.023/GB/month vs the cost of SSD block storage on cloud VMs
If you're also running a WordPress Multisite network, media offloading becomes nearly mandatory at scale — the uploads directory fragmentation across sites creates significant I/O overhead. The WordPress Multisite hosting setup guide covers how storage architecture interacts with Multisite directory structure.
Choosing a Storage Backend
AWS S3
The default choice. Mature ecosystem, deep plugin support, and tight integration with CloudFront for CDN delivery. Pricing is not the cheapest (egress costs add up), but reliability and tooling are unmatched.
When to use S3: Large teams with existing AWS infrastructure, high-compliance environments (S3 has SOC 2, HIPAA BAA available), or sites already using CloudFront.
Cloudflare R2
S3-compatible API with zero egress fees. This is the key differentiator — you pay for storage ($0.015/GB/month) and Class A/B operations, but not for bandwidth out. For media-heavy sites, this can cut storage costs by 60–80% compared to S3 + CloudFront egress.
When to use R2: Sites with high read volume, image-heavy content, or video. R2 sits on Cloudflare's network, so pairing it with Cloudflare CDN is zero-config.
Backblaze B2 + Bunny.net
B2 offers $0.006/GB/month storage (the cheapest tier tested in production). Bunny.net has free egress from B2 due to their bandwidth alliance. Total cost for 1TB stored + 5TB served: roughly $6 storage + $5 CDN = $11/month vs ~$90 on S3 + CloudFront for the same volume.
When to use B2 + Bunny: Budget-conscious setups, agencies managing many small client sites, or anyone wanting the lowest possible bill without sacrificing CDN performance.
| Backend | Storage Cost | Egress Cost | S3-Compatible API | Best For |
|---|---|---|---|---|
| AWS S3 | $0.023/GB/mo | $0.085–0.09/GB | Native | Enterprise, compliance |
| Cloudflare R2 | $0.015/GB/mo | $0 | Yes | High-traffic, image-heavy |
| Backblaze B2 | $0.006/GB/mo | $0.01/GB (free w/ Bunny) | Yes | Cost-sensitive, agencies |
| DigitalOcean Spaces | $0.02/GB/mo | $0.01/GB | Yes | DO-native stacks |
Plugin Options: What Actually Works
WP Offload Media (Delicious Brains)
The production standard. It handles initial upload offloading, rewrites all URLs (including srcset), copies existing media to your bucket, and has a bulk migration tool that doesn't timeout on large libraries. It supports S3, R2, DigitalOcean Spaces, and GCS.
Pricing starts at $149/year for one site. For agencies managing multiple client sites — especially if you're running the WordPress agency workflow at scale — the unlimited license at $399/year pays for itself quickly.
Key settings to configure:
- Enable "Remove files from server" only after confirming bucket delivery works
- Enable "Rewrite media URLs" before the bulk migration so existing content updates in place
- Set cache headers on the bucket (Cache-Control: max-age=31536000, immutable for versioned filenames)
Media Cloud (Interfacelab)
More feature-rich than WP Offload Media — includes image processing via Imgix/Cloudinary, video processing, and a document library. Useful if you need server-side image transforms (resize on demand, format conversion to WebP/AVIF).
Free tier covers basic S3 offloading; the Pro plan ($49/year) adds video and Imgix integration.
What to Avoid
Lightweight plugins like "WP Media Folder" S3 add-ons or "Offload Media Lite" clones from the directory often miss srcset rewriting, break on large libraries, or fail silently when bucket permissions change. Don't find this out in production.
Step-by-Step: S3 + CloudFront Setup
1. Create an S3 Bucket
aws s3api create-bucket \
--bucket your-site-media \
--region us-east-1
Block public access at the bucket level. You'll serve files through CloudFront with an Origin Access Control (OAC) policy — not via public bucket URLs.
2. Create an IAM User with Minimal Permissions
Your plugin only needs these S3 actions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObjectAcl",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-site-media",
"arn:aws:s3:::your-site-media/*"
]
}
]
}
Never use root credentials. Generate an access key for this IAM user only.
3. Configure CloudFront Distribution
- Origin: your S3 bucket endpoint (not the website endpoint)
- OAC: attach an Origin Access Control so CloudFront can read private objects
- Cache behavior: Cache-Control headers passthrough, compress objects automatically
- Alternate domain:
media.yourdomain.comwith an ACM SSL certificate
4. Configure WP Offload Media
In WordPress admin → Offload Media → Settings:
- Provider: Amazon S3
- Bucket:
your-site-media - Custom domain:
https://media.yourdomain.com - Enable: Rewrite Media URLs, Copy to S3 on Upload
- Disable: Remove Local Media (until you've verified delivery)
5. Bulk Migrate Existing Media
WP Offload Media's migration tool runs in the background via Action Scheduler. For large libraries (10k+ files), trigger it via WP-CLI to avoid browser timeouts:
wp offload-media sync-media-to-provider --all-images
Monitor with:
wp action-scheduler list --status=pending --format=count
6. Verify and Remove Local Files
After migration completes:
- Spot-check 20–30 attachment URLs in the database — confirm they point to your CDN domain
- Test srcset attributes on several image blocks
- Check WooCommerce product images if applicable
- Once confirmed: enable "Remove Local Media" in plugin settings
Cloudflare R2 Setup (Shorter Path)
R2's zero-egress model makes it attractive, and setup is faster if you're already on Cloudflare.
# Install Wrangler
npm install -g wrangler
# Create R2 bucket
wrangler r2 bucket create your-site-media
# Enable public access via Cloudflare CDN
# Done in Cloudflare dashboard: R2 → Bucket → Settings → Public Access
In WP Offload Media:
- Provider: Cloudflare R2
- Account ID: from your Cloudflare dashboard
- Access Key / Secret: create in R2 → Manage API Tokens
- Custom domain:
media.yourdomain.com(CNAME to your R2 bucket's public URL)
The CDN delivery uses Cloudflare's network automatically — no separate CloudFront setup needed. This is the setup I recommend for most new projects where there's no existing AWS dependency.
URL Rewriting: The Critical Detail
The most common failure mode is incomplete URL rewriting. WordPress stores attachment URLs in multiple places:
wp_posts.guid— canonical attachment URL (don't rewrite this)wp_posts.post_content— image blocks, classic editor embedswp_postmeta._wp_attachment_metadata— srcset datawp_postmeta._wp_attached_file— relative path used by plugins- Theme options, page builder meta, widget data
WP Offload Media handles the first four reliably. Page builder data (Elementor, Bricks) sometimes stores absolute URLs in serialized meta — run a search-replace with WP-CLI after migration:
wp search-replace 'https://yoursite.com/wp-content/uploads' \
'https://media.yourdomain.com' \
--all-tables \
--precise \
--report-changed-only
Always take a full database backup before running search-replace. If your hosting doesn't give you one-click snapshots, migrating to managed hosting is worth the conversation.
Performance Impact: Real Numbers
According to Cloudflare's 2024 connectivity report, CDN delivery reduces media TTFB by an average of 65% for visitors more than 1,000 miles from the origin server. For a US-hosted site serving European traffic, that's the difference between 400ms and 140ms on image load.
The origin-side benefit is equally meaningful: removing media serving from nginx frees worker connections for PHP-FPM processes. On a site doing 50k pageviews/day with an average of 8 images per page, that's 400k fewer file-serving requests hitting your web server daily.
You can stack this with CDN-level caching for dynamic content — our WordPress CDN setup guide covers cache rules for HTML vs static assets, which is a different configuration problem than object storage offloading.
Common Mistakes and How to Avoid Them
Removing local files too early: Run in offload-only mode for at least one full backup cycle before deleting local copies. If something goes wrong with the bucket, you want a local fallback.
Ignoring CORS headers: If your theme loads fonts or SVGs from the media domain via fetch() or CSS url(), you'll hit CORS errors. Set Access-Control-Allow-Origin: * (or your site domain) in your bucket/CDN CORS policy.
Not setting cache headers: Default S3 headers don't include Cache-Control. Without it, browsers re-request every image on every visit. Set Cache-Control: public, max-age=31536000, immutable for WordPress media (filenames are content-addressed by default).
Serving WebP without format negotiation: If you want CDN-level WebP conversion (serving .webp to supporting browsers, .jpg to others), you need either Cloudflare's Polish feature, Imgix, or a CDN that supports Accept header-based routing. Don't assume your CDN does this by default.
Who Should Offload Media
Media offloading pays off fastest for:
- High-traffic sites (50k+ pageviews/month) where origin bandwidth costs are real
- WooCommerce stores with large product image libraries — see also the WooCommerce HPOS migration guide for complementary performance work
- Media publishers with video or large PDF libraries
- Agencies managing many client sites — centralizing media to one storage account simplifies backup and reduces per-site server storage costs
For low-traffic sites under 10k pageviews/month, the operational complexity may outweigh the gains. A well-configured CDN proxy (Cloudflare proxying your origin) often gets you 80% of the benefit with zero setup. Freelancers managing a small portfolio of client sites might find that managed WordPress hosting with built-in CDN covers the use case without the additional infrastructure layer.
If you're on a TopSyde managed WordPress plan (starting at $89/mo), CDN integration is included and we can assist with the object storage configuration as part of onboarding — particularly useful if you're migrating an existing site with a large media library.
Frequently Asked Questions
Does WordPress media offloading break existing image URLs?
Only if you enable URL rewriting before migrating existing files, or skip URL rewriting entirely. Use WP Offload Media's migration tool to copy existing files first, then enable rewriting. For large libraries, run the migration via WP-CLI to avoid browser timeouts. Take a full database backup before any search-replace operations.
Which is cheaper: S3 + CloudFront or Cloudflare R2?
For most sites, Cloudflare R2 is significantly cheaper because it charges zero egress fees. At 1TB stored and 10TB served per month, R2 costs roughly $15–20 total vs $250–350 for S3 + CloudFront egress. The cost difference grows with bandwidth volume. S3 only wins on compliance certifications and ecosystem depth.
Can I offload media on WordPress Multisite?
Yes, and it's one of the strongest use cases. WP Offload Media supports Multisite with per-subsite bucket paths (e.g., bucketname/site-2/2026/01/image.jpg). You can use a single bucket for the entire network or separate buckets per site — the plugin handles path prefixing automatically. Review your Multisite server configuration before enabling offloading to ensure consistent domain mapping.
Topics

DevOps & Security Lead
12+ years DevOps, Linux & cloud infrastructure certified
Marcus leads infrastructure and security at TopSyde, managing the server fleet and AI monitoring systems that keep client sites fast and protected. Former sysadmin turned WordPress hosting specialist.



