Fixed a post_content corruption bug in the content_save_pre filter (revert_cdn_urls_in_content). In Worker mode (the default), when the CDN host equals the WordPress site host, the previous logic computed a CDN prefix without the /wp-content/uploads/ path segment and then ran str_replace(cdn_prefix, local_prefix, $content), which prepended /wp-content/uploads/ to every internal URL in the post on save. This doubled the path on media URLs (/wp-content/uploads/wp-content/uploads/…) and injected the path on non-media internal links (/lm002/ became /wp-content/uploads/lm002/).
Fixed a related URL-corruption bug in the output-time encode_upload_url_paths rewriter. The same too-short prefix computation also misled its foreign-domain detection and stripped the /wp-content/uploads/ segment when rewriting legacy/foreign CDN URLs to the current CDN host. Visible on sites with legacy-domain URLs in content (e.g. static.example.com → www.example.com) or already-rewritten URLs from a separate CDN host.
Both functions now route through a shared compute_current_cdn_prefix() helper that correctly returns {cdn_base}/{r2_prefix}/ in Direct mode and {cdn_base}/wp-content/uploads/ (host-swapped if needed) in Worker mode. When the CDN URL prefix equals the local prefix, the filters short-circuit.
The original bug was introduced by the 2026-04-29 refactor that wired force_prefix_in_url through the URL rewriter and made get_url_prefix() return empty in Worker mode; the two affected functions then started producing wrong results because they relied on get_url_prefix() to fill in the URL’s path segment, which only worked in Direct mode. Sites in Direct mode (force_prefix_in_url = true) were never affected.
3.3.3
Applied late output escaping at every echo site in the scanner and post-content-scanner admin UI, replacing earlier “pre-escaped helper returns HTML” patterns with helpers that return data and inline esc_attr() / esc_html() at the echo site (WP.org review hardening).
Refactored render_pagination_buttons() to print directly with per-attribute escaping instead of building an HTML string and echoing it whole.
Hardened inline JSON data islands (<script type=”application/json”>) with JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT flags.
Fixed Media Library Scanner “All” tab count: it now shows the number of distinct attachments with issues (matching the rendered table), rather than the sum of per-category counts (which double-counted attachments matching multiple categories).
Cron pipeline now detects silent size- and WebP-generation failures (transient image-editor or Cloudflare errors that previously left records marked completed despite producing nothing) and converts them to a real failure status, allowing the existing retry logic to recover the attachment on subsequent cron ticks.
Registration no longer guesses original_location from the current Original File Handling setting; it leaves the field NULL until verified. This prevents the Orphan Scanner from flagging legacy originals as orphans on sites where the setting was changed at some point — the scanner’s NULL branch indexes both possible locations until the Media Library Scanner aligns the record to observed state.
AJAX nonces on the Media Manager page are now refreshed continuously via the WordPress Heartbeat API. A tab left open beyond the 24-hour nonce-expiry window no longer silently fails on actions; the nonce stays valid for the life of the user’s login session. A safety-net handler also surfaces a clear “session expired, please reload” message if the refresh ever fails (e.g., browser-throttled background tab).
3.3.2
Fixed Move to /originals/ original file handling: the unscaled original is now actually moved to wp-content/uploads/originals/<dir>/ locally, mirroring the bucket placement. Previously only the bucket copy moved while the local copy stayed alongside the master.
Fixed Media Library Scanner contradictory report on original-image rows: the location column now shows presence at the intended folder only (no longer reports a stale legacy copy as “present”), and the misplaced badge label was clarified to “Original: stale copy in old folder.”
Fixed scanner double-emit of “Original missing” + “Original misplaced” badges on the same file when the original was at a legacy location.
Fixed Undefined array key “status” PHP warning during quarantine restore.
Suppressed harmless Table doesn’t exist errors logged when the WordPress Plugin Check tool runs the plugin under its alternate wp_pc_ table prefix.
Removed verbose per-file scanner diagnostics from debug.log (kept once-per-scan summaries).
Quieted per-image cron pipeline debug logs by default; opt back in via define(‘SQMEDIA_DEBUG_VERBOSE’, true); in wp-config.php when investigating cron behavior.
3.3.1
Renamed remaining STATICQ_* wp-config.php constants to the SQMEDIA_* prefix for project-wide naming consistency (SQMEDIA_R2_ACCOUNT_ID, SQMEDIA_R2_ACCESS_KEY_ID, SQMEDIA_R2_SECRET_ACCESS_KEY, SQMEDIA_R2_BUCKET, SQMEDIA_R2_PREFIX, SQMEDIA_CF_TOKEN, SQMEDIA_SKIP_DIRS). Update wp-config.php on upgrade.
Escaped CDN URLs injected into <img> src and srcset via the the_content filter and the frontend output buffer with esc_url(), hardening rendered output.
Refactored frontend output buffer to a plain ob_start() / ob_get_clean() pair on template_redirect / shutdown (replacing the callback-based buffer) for clearer pairing and added a buffer-level guard so foreign nested buffers cannot trigger our close.
Made the JS-template ob_start() / ob_get_clean() pairing in the scanner UI renderer trivially auditable by capturing the buffer to a local variable before passing it to wp_add_inline_script().
Included the plugin’s top-level composer.json in the WordPress.org distribution zip.
3.3.0
Renamed internal identifiers from sq_ to sqmedia_ to meet WordPress.org prefix uniqueness requirements. Pre-3.3 dev-build installs: settings reset on upgrade — reconfigure R2 credentials and re-run the Cloudflare Worker wizard.
Removed Google Cloud Storage support from the free plugin; R2 is now the only storage provider.
Hardened JSON input sanitization in orphan quarantine AJAX handlers.
Excluded vendor binaries and build scaffolding from the distribution zip.
3.2.0
Added Post Content Scanner — detects wrong domain URLs, stale size references, deleted attachments, and broken URLs in post content.
Cloudflare R2 is now the default storage provider.
All features fully available — no restrictions.
Added table backup option before applying scanner repairs.
3.1.0
Added Cloudflare Worker deployment wizard.
Improved <picture> tag delivery with format fallbacks.
Added Media Library Scanner with disk and bucket cross-referencing.
Added orphan file detection and cleanup.
Removed debug logging for cleaner production output.