Skip to content

Changelog

The latest version is 0.1.3. Update from Dashboard → Updates in WordPress, or download it from WordPress.org.

Faster offloading of existing media with a new Transfer Speed setting, a Tools screen to apply a changed retention policy to offloaded media, a new wp omnioffloader retention command, plus security and performance fixes. Recommended for all users.

  • New documentation site at omnioffloader.vercel.app: setup guides for every storage provider, every feature, the WP-CLI commands and the developer hooks. A Documentation link on the Plugins screen opens it.
  • New Tools screen: apply the saved retention policy to media that was offloaded under an earlier policy. Switching from Retain Local Files to Smart Local Cleanup or Full Cloud Migration now frees space for existing media too, not just new uploads.
  • Every local file is checked in cloud storage by size before it is deleted. Files missing from the bucket (for example thumbnails regenerated after the offload) are uploaded first; a file whose cloud copy differs is uploaded again, and kept and logged only if that fails. An attachment only counts as updated once every file was handled, so failures are retried on the next run.
  • Moving to a looser policy downloads local copies again while the attachment stays offloaded.
  • Preflight shows how many attachments need updating and roughly how much space will be freed or downloaded. Full Cloud Migration asks for confirmation before originals are removed.
  • Runs in the background with the same progress bar, activity log, batch sizes and cancel button as Bulk Offload and Bring Back.
  • Settings shows a notice after saving a new policy when existing media still follows an older one.
  • New WP-CLI command: wp omnioffloader retention [--policy=<0|1|2>] [--ids=<ids>] [--limit=<number>] [--dry-run] [--yes] [--force]. wp omnioffloader status now reports media that follows an older policy.
  • New filter omnioffloader_should_apply_retention to keep specific attachments’ local files.
  • WP-CLI reference on the Tools screen: every command with its options, copy buttons and a link to the matching admin screen.
  • Offload, Bring Back and retention jobs can no longer run at the same time. This applies whether a job was started from the admin screens, the Media Library or WP-CLI.
  • Offloading media that is not in the cloud yet is now faster. New uploads were already offloaded instantly in the background; the big win is for your existing library — Bulk Offload, Media Library bulk actions and wp omnioffloader offload. An attachment’s files (original and every generated size) now upload side by side instead of one after another, and at any speed above Low a few attachments share one batch, with their main files confirmed in the bucket together. Everything still runs inside one PHP process, and each attachment is only marked offloaded once every one of its files uploaded and its main file is confirmed in the bucket.
  • New Transfer Speed setting: Low (one file at a time, lowest resource use), Balanced (3 files, default), Fast (6 files, for VPS and dedicated servers) or Custom (any number from 1 to 10). WP-CLI offloads use the same setting. Large multipart uploads follow the same limit. Developers can override it with the omnioffloader_transfer_concurrency filter.
  • Bring Back is faster: files that are still on your server are checked against the cloud with one request per attachment instead of one per file, and missing files download side by side at the same Transfer Speed. Each file is still written to a temporary file and moved into place only when complete.
  • Background passes run longer where the host allows it (up to 40 seconds, based on max_execution_time), so WordPress reloads less often during a bulk job.
  • Uploads no longer add the optional CRC32 checksum that recent AWS SDK versions send by default. That saves one read of every file, and several S3-compatible services reject the header.
  • Offloading and bringing back media, from the Media Library links, bulk actions or REST, is now limited to administrators, like the plugin’s settings and bulk screens. Before, any user who could upload files could offload or bring back anyone’s media and delete its cloud copies. Other users keep their normal WordPress rights: they upload and delete their own media, and with Mirror Delete on, deleting also removes the cloud copy.
  • Bring Back from the Media Library screen now follows the “delete from cloud after restore” setting.
  • Local files are only deleted inside the uploads folder, and restores refuse paths containing .. before creating any directory. File paths from attachment metadata that point outside the uploads folder are ignored for upload, cleanup and restore.
  • The Media Library status check only returns media the user can edit, and only shows the bucket name to administrators. Only administrators’ status checks process pending background uploads.
  • Error messages no longer include absolute server paths.
  • Media Library status tooltips are escaped.
  • REST parameters are validated against their schema (invalid limits, page sizes or actions now return 400), and the Media Library bulk endpoint accepts at most 1,000 IDs.
  • “Remove all data on uninstall” keeps the offload records of media that exists only in the cloud, so it can still be recovered after reinstalling.
  • Front end: media blocks are the only blocks whose IDs are looked up. A navigation menu no longer costs a database query per item.
  • Front end: all attachments used by image, gallery, video, audio, file, cover and media-text blocks are loaded in one batch before the blocks render, so a page costs no extra queries per image (a 6-image gallery went from 11 queries to 6, the same as without the plugin).
  • Front end: video and audio links in content are resolved with one query per post instead of one full scan per link, and cached for the rest of the page even when the content is rendered again (for example by an SEO plugin).
  • The Bring Back and Tools screens reuse the cached preflight instead of rescanning the whole library every 6 seconds during a run.
  • The Media Library status script no longer polls the server on every editor load or window focus when there is nothing to update.
  • Each Media Library status check reads the bulk job state once (2 queries when idle instead of 6–8).
  • wp omnioffloader retention --dry-run and the offload/restore dry runs load attachments 200 at a time instead of one by one.
  • Uploading a file whose name is already taken checks all existing names with one query instead of one per “-1”, “-2”… suffix.
  • Clearing the stats cache costs half as many queries, and background uploads clear it once per batch instead of once per file.
  • The post-activation redirect check only runs on the Plugins screen instead of on every admin request.
  • Bulk jobs write their progress once per batch instead of two or three times per attachment, which cuts database writes sharply on large libraries.
  • The Bring Back preflight reads only the attachment metadata it needs, 500 attachments per query, and keeps its estimate for an hour (it is refreshed whenever media is offloaded, brought back or deleted). Free disk space is always current.
  • The Media Library bulk action loads the selected attachments in a few queries instead of several per item.
  • On libraries of 5,000 items or more, the Bulk Offload and Bring Back screens suggest the matching WP-CLI command.
  • Bulk jobs: two background workers could pick up the same batch at the same moment and process items twice. A database lock now lets only one worker run a queue at a time.
  • Multisite: each site now has its own queues. Before, all sites in a network shared one queue.
  • Only administrators’ Media Library views keep bulk jobs moving; other users’ views only read their status. Idle status responses no longer repeat the last run’s numbers.
  • WP-CLI: locks are renewed after every file and released on Ctrl-C. --limit must be a positive number (a typo no longer means “the whole library”), --ids and --all can’t be combined, and skipped IDs are reported. restore stops after 10 failures in a row, status exits with an error code when the bucket is unreachable, and retention --dry-run supports --format=json|csv.
  • REST: settings and credentials are validated against their expected shape, the error list supports paging, and responses always use consistent types.
  • Accessibility: card choices support arrow keys, progress bars are labelled, and screen readers hear when a run starts or finishes. The admin screen now loads right-to-left styles for RTL languages and respects reduced motion.
  • The Dashboard only shows saved settings, the Settings page warns before you leave with unsaved changes, and error messages stay until dismissed.
  • Attachment offload records can no longer be edited as custom fields.
  • A missing vendor folder shows an admin notice instead of a fatal error.
  • Bulk jobs no longer stop for good on a stretch of broken attachments. Before, 10 failures in a row, such as files missing from the server, stopped the run. Because those attachments stayed first in line, every restart stopped on them again. Now the connection to the storage provider is checked first. If it answers, the run carries on and the failures stay in the error log. It still stops after 100 failures in a row. Attachments that failed before now wait until the end of the next run. The same applies to wp omnioffloader offload and restore.
  • When the storage provider can’t be reached, the stop message now includes the provider’s error.
  • Cancelling a bulk job now stops the background worker right away. Before, the worker kept going until the end of its pass (up to 40 seconds, and longer with Redis or Memcached). Because the job already showed as cancelled, another job could start while it was still running.
  • Media Library items no longer stay stuck on “Queued” or “Offloading…” after a job was cancelled or stopped, or after the server killed the worker mid-file.
  • Bring Back stops with a clear “out of disk space” message before the disk fills up, instead of failing downloads and blaming the connection. The attachment stays in the cloud, and the next run picks it up again.
  • Bring Back no longer counts an attachment as failed when it was already brought back while it waited in the queue.
  • Attachments excluded by the omnioffloader_should_offload_attachment filter now show as “Skipped” in the activity log and WP-CLI summary instead of as failures, and no longer keep a “Queued” badge.
  • A very long single transfer (a multi-GB file on a slow connection) no longer lets the job lock expire mid-file. The lock is renewed while bytes are moving, so another job can’t start alongside it. New action omnioffloader_transfer_heartbeat fires every 30 seconds during uploads and downloads.
  • Partial downloads left behind when the server stopped a Bring Back mid-file are now cleaned up.
  • The Bring Back download estimate now counts only the files that are actually missing from the server. Before, it also counted files your retention policy kept on the server, so a site using Retain Local Files saw its whole library as the download size and could get a false “not enough disk space” warning. Attachments uploaded before WordPress 6.0, which have no recorded file size, are now reported separately instead of silently counting as zero. A note also explains that free disk space is what the server reports, and a hosting plan’s storage quota may be lower.
  • Shared hosting with a storage quota (cPanel, CloudLinux): PHP can only see the whole server disk, not your plan’s quota. Bring Back and retention updates now notice when the server refuses to save a file (“Disk quota exceeded”, “No space left on device”) and stop right away with a clear message. Before, they kept failing until the failure limit and blamed the attachments. Nothing is lost: attachments that were not brought back stay in the cloud. New filter omnioffloader_free_disk_space lets a host or developer supply the real space left in the quota.
  • Mirror Delete is now on by default for new installs, so an attachment deleted in WordPress no longer stays publicly reachable, and billed, in the bucket. Existing sites keep their current setting. It never runs on a staging or development copy (Safe mode).
  • New: Safe mode for staging, development and local copies. A copy of a live site (made with a backup or migration plugin, a host’s staging tool, or on a local machine) is recognised automatically, and nothing is uploaded to or deleted from the live site’s cloud storage from it. Before, a copy could overwrite files in the live bucket with its own uploads and, with Mirror Delete on, delete files the live site still served. Media already in the cloud keeps loading and new uploads stay local. Bring Back is hidden and turned off on a copy unless you allow it in Settings (“Allow Bring Back on a copy”), and then it only downloads, never deleting from the cloud. Detection uses the site address, WP_ENVIRONMENT_TYPE and local addresses; a banner explains why, with a “This is the live site” button for a real domain move. Control it with the new Settings option “Protect the live site when this is a copy” or the OMNIOFFLOADER_SAFE_MODE constant. wp omnioffloader status shows the mode; wp omnioffloader offload and restore refuse to run in Safe mode (restore works when Bring Back is allowed, but never with --delete-cloud).
  • cPanel hosting: the Bring Back screen now shows the space left in your hosting plan (for example “9.3 GB of 10 GB”), read from cPanel’s own quota record, together with when cPanel last updated it. Before, it showed the whole server disk (often terabytes). No shell commands are used. The disk check during Bring Back uses the quota too, as long as cPanel updated it within the last hour.
  • The retention policy update now checks the storage connection before stopping on repeated failures, like Bulk Offload and Bring Back.
  • The Dashboard’s local media count now matches what Bulk Offload processes (trashed attachments and entries without a file are not counted), so it reaches 0 after a full run.
  • Provider errors are only written to the PHP error log when WP_DEBUG is on (filter omnioffloader_log_provider_errors); they are always recorded on the attachment.
  • Retention: when a cloud copy differs in size from the server file (for example after an image optimizer shrank the thumbnails), the server file is uploaded again and verified, instead of the run stopping with a misleading connection error.
  • Retention: attachments excluded with the omnioffloader_should_apply_retention filter no longer show as “needs updating” forever.
  • Progress polling no longer runs two timers at once, keeps polling after you leave the page, or restarts after Cancel.
  • Deactivating the plugin now also clears the background upload queue and its scheduled event.
  • Counts use proper singular/plural translations, and the activity log’s “Attachment #” label is translatable.
  • New filter omnioffloader_media_block_names to register third-party media blocks.
  • The omnioffloader_after_offload action fires again after an attachment is offloaded and verified.
  • New: separate sites can safely share one bucket and one CDN address, each in its own Path Prefix folder. Each site marks its folder with a small .omnioffloader-site.json file. Before, two sites using the same folder overwrote each other’s files, and Mirror Delete on one site deleted files the other still served. Settings now refuses a folder another site uses. A site that finds another site’s marker in its folder switches to Safe mode and stops uploading and deleting. It shows a banner with Choose a Path Prefix and This folder belongs to this site. Bulk Offload and retention runs stop with a clear message instead of failing item by item. Test Connection warns about a taken folder, wp omnioffloader status reports it, and a confirmed domain move (“This is the live site”) takes the folder’s claim along. The folder is checked on the first upload or delete after an update or settings change, then once a day. Developers can turn the check off with the omnioffloader_check_folder_claim filter.
  • Safe mode and the folder check identify a site by its saved Site Address, so multilingual sites with a different domain per language (WPML, Polylang) are never mistaken for a copy of themselves or for another site.

Adds custom batch quantity limits and presets for Bulk Offload and Bring Back, proportional Bring Back disk estimation, WP-CLI restore –limit support, and fixes real-time progress updates under LiteSpeed Cache, Breeze, and persistent Object Cache (Redis/Memcached). Recommended for all users.

  • Custom Batch Size & Quantity Limits: users can now choose between processing the entire media library (“All Media”) or specifying a custom number of attachments for both Bulk Offload and Bring Back.
  • Interactive Scope Selector: added modern radio card controls with quick preset chips (5, 10, 25, 50, 100, and All) and clean, centered numeric input field with unit labels.
  • Dynamic Action Buttons: buttons on Offload and Bring Back screens dynamically display target batch counts (e.g. “Start Bulk Offload (5 of 10)” / “Start Bring Back (5 of 10)”).
  • Proportional Download Estimation: Bring Back calculates and displays the exact disk space required for the selected custom batch size alongside available server free space before starting.
  • REST API limit parameter: registered sanitized and validated limit parameter on POST /omnioffloader/v1/offload/bulk and POST /omnioffloader/v1/bring-back/bulk.
  • WP-CLI --limit=<number>: added --limit=<number> option to wp omnioffloader restore matching wp omnioffloader offload --limit=<number>, enabling batch restores from the terminal.
  • LiteSpeed Cache & Breeze compatibility: added strict anti-caching headers (no-store, no-cache, must-revalidate, max-age=0, Pragma: no-cache, Expires: 0, and X-LiteSpeed-Cache-Control: no-cache) to all plugin REST endpoints.
  • Added DONOTCACHEPAGE constant definition and litespeed_can_cache / breeze_can_cache bypass filters to prevent web-server level caching of dynamic REST routes.
  • Client-side request cache-busting: added microsecond timestamp query parameters (_t=${Date.now()}) and cache: 'no-store' to all API polling and data fetch calls, preventing browsers, edge proxies, and reverse proxies from serving stale HTTP 200 responses.
  • Persistent Object Cache synchronization: added automatic cache invalidation (wp_cache_delete) for queue state options prior to reading, ensuring multi-worker Redis and Memcached environments observe real-time bulk progress without delay.
  • Multi-layer transient flushing: enhanced stats and preflight cache flushes to clean both object cache groups (transient) and fallback options table entries.
  • Added force-refresh support (?refresh=1) to /core/stats and /bring-back/preflight, allowing immediate post-run verification directly from the database.
  • Standardized badge formatting across option cards to ensure identical phrasing (%d items / %d item) and consistent badge styling.
  • Active selection badge accent transitions and crisp input alignment.
  • Real-time Bulk Offload & Bring Back logs: Activity log and progress bar now update live with each poll under any caching plugin or proxy configuration.
  • Live-updating summary chips: Offloaded and Local attachment counts now update dynamically in the summary header as items are processed during active bulk runs.
  • Fixed “Start Bring Back (20)” button count remaining frozen after media restoration; the preflight check now automatically refreshes and transitions to “No Offloaded Media to Restore”.
  • Fixed “All Media Offloaded” state not displaying immediately upon offload completion; stats refresh now promptly detects 100% completion and renders the success banner.

Adds presets for Amazon S3, DigitalOcean Spaces, Backblaze B2, and Wasabi; real-time Media Library live status badges, meta box, and bulk actions; multipart uploads for large files; and major background queue stability and performance improvements. Recommended for all users.

  • Multi-provider expansion: added dedicated provider presets for Amazon S3, DigitalOcean Spaces, Backblaze B2, and Wasabi, plus a generic S3-Compatible option for MinIO and other custom endpoints.
  • Dedicated OmniOffloader Meta Box on edit attachment screen (post.php) with cloud storage status, provider, bucket, offloaded date, CDN status, and one-click actions.
  • Media Library live status integration: added minimal status icon column (cloud, spinner, warning, dash) in list view and live badges in grid view modals.
  • Media Library bulk actions: added “Offload to Cloud” and “Bring Back to Server” to bulk action dropdowns with instant real-time UI updates.
  • Dynamic row actions: attachment row actions beneath titles now dynamically reflect live states (“Offloading…”, “Restoring…”, “Queued…”, “Bring Back”, “Offload now”, “Retry Offload”).
  • Non-blocking uploads: introduced AsyncUploadProcessor to offload new uploads in the background without delaying server responses.
  • Content URL rewriting expansion: rewrites media URLs inside Gutenberg blocks (Video, Audio, File, Media & Text, Cover), Classic Editor HTML media tags, and WordPress audio/video shortcodes.
  • Added multipart uploads for files above 64 MB, supporting large transfers beyond 5 GB without memory exhaustion.
  • Added --limit=<number> to wp omnioffloader offload for running bulk jobs in manageable chunks.
  • Hardened bulk queue worker (AbstractBulkProcessor): claims items before processing so a worker killed by host time limits drops the item instead of getting stuck in an infinite retry loop.
  • Extended queue process lock from 60 seconds to 10 minutes (renewed after each item) to prevent duplicate workers from running on small hosting pools.
  • Consecutive failure circuit breaker: bulk jobs halt gracefully with clear error logging after 10 consecutive failures instead of grinding through the whole library.
  • Fixed Bring Back restore logic: verified files are completely restored on disk before updating metadata or pruning cloud copies.
  • Prevented stale activity logs from flashing previous runs when starting a new Offload or Bring Back operation.
  • Fixed Cancel button not taking effect on hosts with blocked internal loopback requests.
  • Fixed stranded queue rows remaining in the database upon deactivation/uninstall.
  • Added connection timeouts to all storage requests to prevent hanging PHP-FPM processes.
  • Optimized status polling with delta responses, slashing payload size by ~94% and avoiding unnecessary DOM re-renders.
  • Integrated Page Visibility API (document.hidden) to pause polling and conserve resources when browser tabs are inactive.
  • Throttled database writes on thumbnail generation during bulk transfers.
  • Cached media library statistics once per bulk run instead of querying on every single item.
  • Fixed Bring Back disk preflight query to run efficiently on large media libraries.
  • Bulk jobs now skip trashed attachments and entries with no file on disk.
  • Initial release of OmniOffloader.
  • Multi-cloud offload engine with Cloudflare R2 provider support.
  • Modern React-based settings and control dashboard.
  • Background bulk offload and bulk restore with real-time progress indicators.
  • URL rewriting for post content, attachment URLs, and responsive srcset attributes.
  • Media library filters and individual attachment action controls.
  • Complete WP-CLI commands: wp omnioffloader offload, wp omnioffloader restore, and wp omnioffloader status.