WP Cloud Files is a WordPress plugin that seamlessly integrates your WordPress media library with S3-compatible object storage. It automatically moves uploaded files to S3 and redirects requests to them from there, helping you scale your WordPress site and reduce server storage requirements.
- Automatically upload media files to S3 storage when added to WordPress
- Serve media files directly from S3, saving your server's bandwidth
- Optional direct browser-to-S3 uploads via presigned PUT URLs, bypassing the web server and any upload-size-limited proxy (e.g. Cloudflare's 100 MB cap)
- Support for all standard WordPress image sizes and optimizations
- Generate thumbnails for documents (PDF, Word, Excel, PowerPoint)
- WP-CLI command for migrating existing media library to S3
- Supports WebP and AVIF alternatives when enabled in WordPress
- Handles image editing and regeneration workflows
- WordPress (tested with latest version)
- PHP 8.1 or higher
- S3-compatible storage provider (AWS S3, DigitalOcean Spaces, MinIO, etc.)
- Composer (for installing dependencies)
- Download
wp-cloud-files.zipfrom the latest GitHub Release - In WordPress, go to Plugins → Add New → Upload Plugin and upload the zip
- Define the required constants in your
wp-config.phpfile (see Configuration section) - Activate the plugin through the WordPress admin interface
The release zip bundles all Composer dependencies, so there is no separate composer install
step. Once installed, the plugin checks GitHub for new releases and offers one-click updates
through the normal WordPress Plugins screen.
- Clone the repository into your
/wp-content/plugins/directory - Run
composer installin the plugin directory to install dependencies - Define the required constants in your
wp-config.phpfile (see Configuration section) - Activate the plugin through the WordPress admin interface
Add the following constants to your wp-config.php file:
// S3 API credentials
define('S3_KEY', 'your-access-key');
define('S3_SECRET', 'your-secret-key');
// S3 bucket and endpoint configuration
define('S3_BUCKET', 'your-bucket-name');
define('S3_ENDPOINT', 'https://your-s3-endpoint.com');
define('S3_PUBLIC_URL', 'https://your-public-bucket-url.com');
// Optional configurations
define('S3_REGION', 'us-east-1'); // Defaults to 'us-east-1' if not specified
define('S3_PATH_STYLE', true); // Set to true for most S3-compatible services (MinIO, DO Spaces)
define('S3_ROOT', ''); // Subfolder within bucket to use as root (optional)
// Direct browser-to-S3 uploads (optional, opt-in)
define('S3_DIRECT_UPLOADS', true); // Enable direct uploads. REQUIRES bucket CORS (see below)
define('S3_DIRECT_UPLOAD_MIN_SIZE', 0); // Min bytes to route direct; 0 = all uploads (default)
define('S3_MAX_UPLOAD_SIZE', 5368709120); // Advertised max upload size in bytes (default 5 GiB)
define('S3_UPLOAD_ACL', 'public-read'); // Sign this ACL on the PUT. Omit for public buckets / R2
define('S3_PRESIGN_EXPIRES', '+15 minutes'); // Presigned URL lifetime (default '+15 minutes')By default, uploads flow browser → web server → S3, so a proxy or PHP limit in
front of your site (such as Cloudflare's 100 MB request-body cap) limits how large
a file can be uploaded. Enabling S3_DIRECT_UPLOADS makes the Media Library
uploader send files straight to your S3 endpoint with a short-lived presigned
PUT URL — the file never passes through the web server.
Because the file bypasses WordPress entirely during upload, image sizes, WebP/AVIF
variants, and document thumbnails are generated afterward by a background WP-Cron
job (wpcf_process_direct_upload): it downloads the original once, runs the normal
WordPress optimization pipeline, and uploads only the derivatives back to S3. Until
that job runs, the full-size original is served in place of thumbnails.
Feature is opt-in. It is disabled unless
S3_DIRECT_UPLOADSis defined and truthy, because direct uploads require bucket CORS to be configured first.
The browser PUTs cross-origin to your S3 endpoint, so the bucket must allow it. Set the allowed origin to your site URL. Example CORS rule:
[
{
"AllowedOrigins": ["https://your-site.com"],
"AllowedMethods": ["PUT"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]Notes:
- Your S3 endpoint must itself accept large request bodies — only the site is behind the size-limited proxy, not the storage endpoint.
- For public buckets / Cloudflare R2 that don't support per-object ACLs, leave
S3_UPLOAD_ACLundefined so nox-amz-aclheader is signed. For AWS S3, MinIO, or DigitalOcean Spaces, set it topublic-read. - Single presigned PUT supports files up to your provider's per-PUT limit (commonly ~5 GB).
Once configured, the plugin automatically handles new media uploads. Just use the WordPress media uploader as usual, and files will be sent to S3 after WordPress processes them.
To migrate your existing media library to S3, use the WP-CLI command:
# Migrate all media files to S3
wp wp-cloud-files migrate
# Migrate and keep local copies
wp wp-cloud-files migrate --keep-local
# Migrate 50 files starting from item 100
wp wp-cloud-files migrate --limit=50 --offset=100
# Force re-upload of all media
wp wp-cloud-files migrate --force
# Process in smaller batches (default is 20)
wp wp-cloud-files migrate --batch-size=10The plugin automatically generates thumbnails for supported document types:
- PDF files
- Word documents (DOCX, DOC, ODT, RTF)
- Excel spreadsheets (XLSX, XLS, ODS, CSV)
- PowerPoint presentations (PPTX, PPT, ODP)
These thumbnails will appear in the WordPress media library just like image thumbnails.
- When media is uploaded to WordPress, the plugin waits for WordPress to complete all processing (image resizing, WebP conversion, etc.)
- Files are then uploaded to your S3 bucket with public read permissions
- Local files are removed to save disk space
- All URLs are automatically rewritten to point to your S3 bucket
Releases are fully automated from Conventional Commits via Release Please. There is no manual version bump — just write conventional commit messages:
fix: ...→ patch release (0.0.x)feat: ...→ minor release (0.x.0)feat!: ...or aBREAKING CHANGE:footer → major release (x.0.0)chore: ...,docs: ...,refactor: ...→ no release on their own
On every push to main, the Release workflow opens (or updates)
a release PR that accumulates the pending changes and previews the next version + changelog.
Merging that PR:
- bumps the version in
composer.json(and the rootVERSIONfile) and updatesCHANGELOG.md; - creates the git tag and a GitHub Release with notes generated from the commits;
- builds the plugin on a Nix runner (
nix build .#zip) and attacheswp-cloud-files.zip(withvendor/bundled) as the release asset.
Client sites then pick up the new version automatically via plugin-update-checker.
The version in the index.php plugin header is stamped from composer.json at build time, so
composer.json is the single source of truth. (A from-source/dev checkout may show a stale
header version until built — the published zip is always correct.)
Repo setting: Settings → Actions → General → Workflow permissions must allow "Read and write permissions" and "Allow GitHub Actions to create and approve pull requests" so Release Please can open the release PR.
For issues, feature requests, or questions, please contact mail@avu.nu or visit https://avunu.io/.
This plugin is licensed under the GPL v3 or later.