{"id":398688,"date":"2026-11-10T23:10:21","date_gmt":"2026-11-10T23:10:21","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/clouder\/"},"modified":"2026-11-10T23:27:30","modified_gmt":"2026-11-10T23:27:30","slug":"devsyed-media-offloader","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/devsyed-media-offloader\/","author":23599132,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.1.3","stable_tag":"0.1.3","tested":"7.1.4","requires":"6.8","requires_php":"7.4","requires_plugins":null,"header_name":"OffloadKit \u2014 Cloud Media Offloader","header_author":"Syed Naqi","header_description":"Offload and deliver WordPress media through your preferred cloud storage provider. Includes Cloudflare R2, WebP, and AVIF support.","last_updated":"2026-11-10 23:27:30","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/github.com\/syednaqi","rating":5,"author_block_rating":0,"active_installs":0,"downloads":77,"num_ratings":1,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.1.2":{"tag":"0.1.2","author":"devsyed","date":"2026-11-10 23:09:57","revision":3795426},"0.1.3":{"tag":"0.1.3","author":"devsyed","date":"2026-11-10 23:27:30","revision":3795444}},"upgrade_notice":{"0.1.3":"<p>New OffloadKit branding. Existing settings and storage connections are preserved.<\/p>","0.1.2":"<p>Updated the plugin name and text domain for the directory submission.<\/p>","0.1.1":"<p>Settings export writes JSON to standard output. Redirect it in your shell if you want a file.<\/p>"},"ratings":{"1":0,"2":0,"3":0,"4":0,"5":1},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3795439,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3795439,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3795439,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3795439,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.1.2","0.1.3"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[3863,3882,84,245358,15376],"plugin_category":[50,59],"plugin_contributors":[291953],"plugin_business_model":[],"class_list":["post-398688","plugin","type-plugin","status-publish","hentry","plugin_tags-cdn","plugin_tags-cloudflare","plugin_tags-media","plugin_tags-r2","plugin_tags-webp","plugin_category-media","plugin_category-utilities-and-tools","plugin_contributors-devsyed","plugin_committers-devsyed"],"banners":{"banner":"https:\/\/ps.w.org\/devsyed-media-offloader\/assets\/banner-772x250.png?rev=3795439","banner_2x":"https:\/\/ps.w.org\/devsyed-media-offloader\/assets\/banner-1544x500.png?rev=3795439","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/devsyed-media-offloader\/assets\/icon-128x128.png?rev=3795439","icon_2x":"https:\/\/ps.w.org\/devsyed-media-offloader\/assets\/icon-256x256.png?rev=3795439","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>OffloadKit manages media offload, delivery and recovery with Cloudflare R2, AWS S3 and DigitalOcean Spaces. A Google Cloud Storage HMAC adapter is also available; configure its interoperability credentials and delivery URL separately.<\/p>\n\n<p>Uploads can be offloaded during upload or processed through the background queue. Failed transfers retain local copies for retry. For public media with retained local files, delivery falls back locally when a storage check confirms the remote object is missing. Storage errors are not treated as proof that an object is missing; cloud-only media requires repair or restoration if its remote copy is lost.<\/p>\n\n<p><strong>Features<\/strong><\/p>\n\n<ul>\n<li>Background offload of new uploads, plus bulk migration of an existing media library.<\/li>\n<li>Optional WebP and AVIF conversion, with per-format quality settings.<\/li>\n<li>Optional <code>&lt;picture&gt;<\/code> output with AVIF and WebP sources.<\/li>\n<li>Non-image files \u2014 video, audio, PDF, archives \u2014 with multipart upload for large files.<\/li>\n<li>File type filter: offload only chosen extensions, or everything except them.<\/li>\n<li>Optional local file deletion, only after every variant is verified in the bucket.<\/li>\n<li>Pull-back: restore everything to local storage and remove the offload metadata.<\/li>\n<li>Optional Cloudflare cache purge by URL.<\/li>\n<li>WP-CLI commands for every long-running operation.<\/li>\n<\/ul>\n\n<p><strong>CDN presets<\/strong><\/p>\n\n<p>Cloudflare setup automatically discovers an active custom domain or enabled R2.dev delivery URL. If neither is available, you can opt in to public R2.dev delivery or configure a custom domain. Existing delivery URLs are preserved when reconnecting the same bucket.<\/p>\n\n<p>Delivery settings include CloudFront, Bunny CDN, Fastly, KeyCDN, Gcore, Cloudflare and Azure Front Door presets, plus a custom URL \/ direct bucket option. Each preset provides a URL example and setup guidance. Configure the CDN origin and HTTPS in its dashboard, enter your complete delivery URL, save, then run Test Connection. Selecting a preset preserves the existing URL and does not provision a CDN or enable CDN-specific cache purging.<\/p>\n\n<p>Legacy URL rewriting covers the full rendered template on WordPress 6.9 and later. On WordPress 6.8, it covers post content and standard WordPress media hooks.<\/p>\n\n<p><strong>Credentials<\/strong><\/p>\n\n<p>Credentials can be defined in <code>wp-config.php<\/code> instead of the database, which is the recommended production setup:<\/p>\n\n<pre><code>define( 'R2CLOUDER_PROVIDER', 'r2' ); \/\/ r2, s3, spaces or gcs\ndefine( 'R2CLOUDER_ACCOUNT_ID', '...' ); \/\/ r2 only\ndefine( 'R2CLOUDER_REGION', 'us-east-1' ); \/\/ AWS region or Spaces datacenter\ndefine( 'R2CLOUDER_ACCESS_KEY', '...' );\ndefine( 'R2CLOUDER_SECRET_KEY', '...' );\ndefine( 'R2CLOUDER_BUCKET', '...' );\ndefine( 'R2CLOUDER_PUBLIC_URL', 'https:\/\/cdn.example.com' );\ndefine( 'R2CLOUDER_CF_PROVISIONING_TOKEN', '...' );\ndefine( 'R2CLOUDER_CF_API_TOKEN', '...' );\n<\/code><\/pre>\n\n<p>The recommended setup accepts one Cloudflare token with Workers R2 Storage Write. OffloadKit can create or select the bucket, derive the S3 credentials, and configure delivery. Zone Read is optional for automatic custom-domain discovery; Cache Purge is optional for purge-by-URL. Manual bucket-scoped credentials remain available under Advanced.<\/p>\n\n<p>DigitalOcean Spaces uses the selected datacenter endpoint (for example https:\/\/nyc3.digitaloceanspaces.com). Google Cloud Storage uses https:\/\/storage.googleapis.com with HMAC interoperability keys, not service-account JSON credentials. Configure a public delivery URL or CDN separately. Managed bucket migration supports R2 and AWS S3 only.<\/p>\n\n<p>Cloudflare\u00ae and Cloudflare R2 are trademarks of Cloudflare, Inc. This plugin is not affiliated with, endorsed by, or sponsored by Cloudflare, Inc.<\/p>\n\n<p>DigitalOcean onboarding can create a new Standard Storage bucket using a Full access Spaces key, or connect an existing bucket. New buckets are private unless you explicitly allow public object reads. OffloadKit prefills the new bucket delivery URL; CDN configuration remains in DigitalOcean. Existing bucket policies are not modified.<\/p>\n\n<h3>External Services<\/h3>\n\n<p>This plugin connects to the storage provider you select after you configure credentials for your own account. CDN preset examples are placeholders. The plugin bundles its own admin assets. Cloudflare tokens can also be supplied for optional setup and cache purging. Deactivating the plugin stops its background requests; previously published media URLs may still be requested by visitors' browsers.<\/p>\n\n<p><strong>1. Cloudflare R2 storage API \u2014 <code>https:\/\/&lt;your-account-id&gt;.r2.cloudflarestorage.com<\/code><\/strong><\/p>\n\n<p>Used to store and manage your media files. The plugin sends the contents of the media files you upload, their file names and MIME types, and an AWS Signature Version 4 request signature derived from the R2 access key you configure. Requests are made when a file is offloaded, verified, listed or deleted, and during the connection test on the settings screen. No visitor data and no personal data beyond what your own media files contain is transmitted.<\/p>\n\n<p><strong>1b. AWS S3 storage API \u2014 <code>https:\/\/&lt;bucket&gt;.s3.&lt;region&gt;.amazonaws.com<\/code><\/strong><\/p>\n\n<p>Used only when you choose AWS S3 as the provider. The same data is sent as for R2: media file contents, file names, MIME types and an AWS Signature Version 4 request signature derived from the IAM access key you configure.<\/p>\n\n<p>AWS terms: https:\/\/aws.amazon.com\/service-terms\/\nAWS privacy notice: https:\/\/aws.amazon.com\/privacy\/<\/p>\n\n<p><strong>Additional storage providers<\/strong><\/p>\n\n<p>DigitalOcean Spaces requests go to https:\/\/.digitaloceanspaces.com. Google Cloud Storage requests go to https:\/\/storage.googleapis.com. Only the selected provider receives media contents, file names, MIME types and signed storage requests, during offloading, verification, listing, deletion and connection tests.<\/p>\n\n<p>DigitalOcean terms: https:\/\/www.digitalocean.com\/legal\/terms-of-service-agreement\nDigitalOcean privacy: https:\/\/www.digitalocean.com\/legal\/privacy-policy\nGoogle Cloud terms: https:\/\/cloud.google.com\/terms\nGoogle privacy: https:\/\/policies.google.com\/privacy<\/p>\n\n<p><strong>1c. Bucket migration services \u2014 <code>https:\/\/api.cloudflare.com<\/code> and <code>https:\/\/datasync.&lt;region&gt;.amazonaws.com<\/code><\/strong><\/p>\n\n<p>Used only when you start a bucket migration. S3 to R2 migrations send the source bucket name, region and access keys, and the target bucket and keys, to Cloudflare Super Slurper. R2 to S3 migrations send the R2 endpoint, bucket and access keys to AWS DataSync, signed with the target IAM keys. Both services copy the files themselves; the plugin sends no file contents.<\/p>\n\n<p>Migration status checks and cancellation also contact the selected migration service. Provider storage and migration charges may apply. The Cloudflare and AWS terms and privacy links in this section apply to these services too.<\/p>\n\n<p><strong>2. Your public delivery domain \u2014 the value you set as the public URL<\/strong><\/p>\n\n<p>Used to serve offloaded files to visitors, and fetched during connection tests to confirm the domain serves the bucket. Visitors' browsers request these URLs directly; their IP address and user agent reach your selected delivery provider.<\/p>\n\n<p><strong>3. Cloudflare API \u2014 <code>https:\/\/api.cloudflare.com<\/code><\/strong><\/p>\n\n<p>Used during the optional one-token setup flow, when cache purging is enabled, or when you choose Connect Domain or Check Status. During setup, the plugin sends the account ID, desired bucket name and location, delivery choice, custom domain and Zone ID when applicable, and the Cloudflare API token needed to authorize those operations. For cache purging, it sends media URLs after an attachment is updated, re-offloaded, or deleted. Manual setup can avoid the provisioning flow entirely.<\/p>\n\n<p>Cloudflare terms of service: https:\/\/www.cloudflare.com\/terms\/\nCloudflare privacy policy: https:\/\/www.cloudflare.com\/privacypolicy\/<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the OffloadKit ZIP through Plugins &gt; Add New &gt; Upload Plugin, then activate it.<\/li>\n<li>Open Settings &gt; OffloadKit and select your storage provider. Use a separate test bucket for your first connection.<\/li>\n<li>Configure credentials and an HTTPS delivery URL, then run Test Connection before enabling media offloading.<\/li>\n<li>Keep local files while verifying uploads and delivery. Enable optional image conversion or local deletion only after checking the results and keeping independent backups.<\/li>\n<\/ol>\n\n<p>Deactivation stops scheduled work and retains settings and media records. Uninstall retains plugin data by default; the optional remove-data setting deletes plugin settings, queue and offload records, but does not delete cloud objects. Pull media back and verify local copies before removing records for cloud-only files.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20a%20cloudflare%20account%3F\"><h3>Do I need a Cloudflare account?<\/h3><\/dt>\n<dd><p>A Cloudflare account is needed only for R2. AWS S3 and DigitalOcean Spaces use their own accounts and credentials. For R2, setup can create or connect a bucket and configure delivery; manual bucket credentials are also supported.<\/p><\/dd>\n<dt id=\"can%20i%20undo%20the%20offload%3F\"><h3>Can I undo the offload?<\/h3><\/dt>\n<dd><p>The pull-back command restores recorded source files to their local WordPress paths. It retains offload metadata if a required download or metadata update fails so you can investigate and retry. Keep independent backups; a missing remote object cannot be recovered without another copy.<\/p><\/dd>\n<dt id=\"what%20are%20the%20bucket%20migration%20limitations%3F\"><h3>What are the bucket migration limitations?<\/h3><\/dt>\n<dd><p>Managed bucket migration supports R2 and AWS S3. Cloudflare Slurper can skip zero-byte objects during S3-to-R2 migration; OffloadKit's destination verification then refuses cutover rather than accepting an incomplete transfer. Automatic repair of skipped empty objects is not available. R2-to-S3 migration requires AWS DataSync and the necessary IAM permissions; this direction has not completed live acceptance testing. Keep source objects and independent backups, and verify migration with a separate test bucket before using it for production media.<\/p><\/dd>\n<dt id=\"does%20this%20work%20for%20protected%20or%20paid%20downloads%3F\"><h3>Does this work for protected or paid downloads?<\/h3><\/dt>\n<dd><p>Private storage delivery supports signed URLs, but it does not implement membership or purchase authorization. WooCommerce and Easy Digital Downloads protected paths are excluded from new offloads, including forced offloads. Existing cloud copies are not automatically made private or removed; audit any previously offloaded protected content. Do not use public media delivery as an access-control system.<\/p>\n\n<p>Private signed URLs expire. Exclude pages and REST responses containing signed media URLs from external full-page, reverse-proxy and CDN caches, and regenerate URLs for each request. OffloadKit sends private\/no-store response headers, but external caches must respect those headers. A cached page can otherwise retain an expired URL or share a valid URL with another visitor. Legacy offload records without provider identity need an ownership audit before switching providers.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.1.3<\/h4>\n\n<ul>\n<li>Renamed to OffloadKit \u2014 Cloud Media Offloader, with updated admin labels and help text.<\/li>\n<li>Existing settings, storage connections and the plugin slug are unchanged.<\/li>\n<\/ul>\n\n<h4>0.1.2<\/h4>\n\n<ul>\n<li>Renamed to Devsyed Media Offloader with the devsyed-media-offloader text domain.<\/li>\n<\/ul>\n\n<h4>0.1.1<\/h4>\n\n<ul>\n<li>Renamed to Flex Media Offloader.<\/li>\n<li>Updated Media Library assets, legacy URL rewriting and WP-CLI settings export.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial development release.<\/li>\n<\/ul>","raw_excerpt":"Offload WordPress media to Cloudflare R2, AWS S3 or DigitalOcean Spaces, with custom delivery, WebP and AVIF support.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/398688","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=398688"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/devsyed"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=398688"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=398688"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=398688"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=398688"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=398688"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=398688"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}