繁體中文版說明請參見 README.zh-TW.md | WordPress on S3 Implementation Guide | WordPress on S3 中文實作指南
A lightweight, dependency-free WordPress plugin to offload your Media Library to Amazon S3 or S3-compatible cloud storage (Cloudflare R2, Backblaze B2, DigitalOcean Spaces, MinIO, etc.).
Note
Looking for Google Cloud Storage (GCS) support instead? Check out our sister project: Just GCS Offload.
Unlike other bloated cloud storage plugins, Just S3 Offload is designed to be as small and efficient as possible. It implements a lightweight S3 REST client in pure PHP using cURL and native cryptographic functions for AWS Signature Version 4 (SigV4), completely bypassing the massive official AWS SDK.
- Zero External Dependencies: Weighs only a few dozen kilobytes. No bulky
vendor/folder or external libraries. - AWS Signature Version 4 (SigV4): Securely signs requests in pure PHP using native cryptographic functions (
hash_hmac). - S3-Compatible Storage Support: Out-of-the-box support for custom S3 endpoints (Cloudflare R2, MinIO, Backblaze B2, DigitalOcean Spaces, etc.) and path-style URLs.
- Automatic Media Offloading: Automatically uploads new images and all generated sub-sizes (thumbnails) to S3 during upload.
- URL & Srcset Rewriting: Seamlessly rewrites image URLs and responsive
srcsetpaths to point to S3 or a custom CDN domain. - Optional Local Cleanup: Optionally deletes the local server copy of uploaded files to save disk space.
- Automatic Deletion: Automatically deletes original and resized files from S3 when an attachment is permanently deleted from the WordPress admin.
- WP-CLI Integration: Provides powerful command-line tools to migrate existing media library items and sync database metadata.
- Connection Test: A simple button in settings to test read/write/delete permissions.
- PHP 7.4 or higher
- PHP Hash extension enabled
- WordPress 6.0 or higher
- Download the latest
just-s3-offload.zipfrom the Releases page. - In your WordPress admin, go to Plugins -> Add New -> Upload Plugin, select the zip file, and click Install Now.
- Activate the plugin.
To ensure your offloaded images are publicly readable, configure your S3 bucket using one of the following methods:
- Keep the bucket default ACL private and disable public access block settings if you wish to serve files directly.
- Configure a bucket policy that allows
GetObjectto*or specific referrers, OR map the bucket to a Cloudflare CDN which handles public serving. - In WordPress settings, keep the "Set Public ACL" option disabled.
- In your bucket settings, ensure ACLs are enabled (e.g., Object writer ownership or Bucket owner preferred).
- In WordPress settings, enable the "Set Public ACL" option. This will apply a
public-readACL header to every uploaded object.
Navigate to Settings -> S3 Offload in your WordPress dashboard to configure the following settings:
- Access Key ID: Your S3 account access key.
- Secret Access Key: Your S3 account secret key.
- S3 Region: The region of your bucket (e.g.,
us-east-1,ap-northeast-1). - S3 Bucket Name: Enter the target S3 bucket name (e.g.,
my-wordpress-bucket). - Custom Endpoint URL: (Optional) For S3-compatible providers, enter the custom endpoint (e.g.
https://<accountid>.r2.cloudflarestorage.comorhttps://s3.us-west-004.backblazeb2.com). - Use Path-Style URLs: Check this if your S3-compatible service requires path-style URLs (
endpoint/bucket/fileinstead ofbucket.endpoint/file). - Folder Path Prefix: (Optional) Subfolder path inside the bucket (e.g.,
wp-content/uploads). Do not add leading or trailing slashes. - Custom Domain / CDN URL: (Optional) Custom domain or CDN mapping (e.g.,
https://cdn.example.com). If empty, the default S3 URL structure will be used. - Cache-Control Header: The Cache-Control header applied to uploaded objects (defaults to
public, max-age=31536000). - Set Public ACL: Check this to set the uploaded objects ACL to public-read.
- Delete Local Files: Check this to delete local copies of files after uploading them to S3. When a local file is needed again, the plugin downloads it back from S3 on demand, so the built-in image editor (crop/rotate) keeps working. Note that the Bulk Upload tool and
wp s3-offload sync-all --delete-localeach carry their own delete-local switch, independent of this setting.
Click Run Connection Test to verify that your credentials and permissions are configured correctly.
-
just_wp_s3_skip_attachment( bool $skip, int $attachment_id )- exclude an attachment from being offloaded. The site icon is skipped by default. -
just_wp_s3_companion_meta_keys( string[] $keys )- the attachment metadata keys treated as companion files sitting next to the main file. Defaults tooriginal_image,source_image,animated_videoandanimated_video_poster. -
just_wp_s3_rehydrate( bool $allow, int $attachment_id )- allow a missing local file to be downloaded back from S3 for the current request.Rehydration is deliberately restricted, because
get_attached_filealso fires on read-only paths.wp_prepare_attachment_for_js()in particular runs once per attachment whenever the Media Library grid or the block editor media picker loads a page of results, so downloading there would turn one screen into dozens of full-size bucket requests. By default only WP-CLI and the built-in image editor are allowed. Tools that genuinely need the local original can opt in:add_filter( 'just_wp_s3_rehydrate', function ( $allow ) { return defined( 'MY_REGENERATING_THUMBNAILS' ) ? true : $allow; } );
For developers and system administrators, the plugin provides custom WP-CLI commands to perform batch operations and migrations.
If you have already copied your files to the S3 bucket using tools like aws s3 sync or rclone, use this command to generate the offload metadata (_wp_s3_info) in the database so WordPress rewrites the URLs.
wp s3-offload sync-metadata [--bucket=<bucket>] [--prefix=<prefix>] [--overwrite]Scan all existing media library attachments, upload them to S3, and update their database records.
wp s3-offload sync-all [--delete-local] [--overwrite]- Use
--delete-localto remove local copies after successful upload. - Use
--overwriteto re-upload files that are already marked as synced.
If you encounter any issues (such as a fatal error or blank screen in the Media Library):
- Enable debugging in your
wp-config.php:define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); define( 'WP_DEBUG_DISPLAY', false );
- Re-trigger the error.
- Check the logs:
- WordPress Debug Log:
wp-content/debug.log - Web Server Error Logs:
/var/log/apache2/error.logor/var/log/nginx/error.log
- WordPress Debug Log:
This project is licensed under the GNU General Public License v2.0 or later (GPL-2.0-or-later).