New in v1.4.2 — Preload wedged by a stale object-cache lock See what's new →
Home/Changelog

What's new.

Every release of Speed of Light, newest first — features, fixes and hardening. We ship small, ship often, and write it all down.

1.4.2

LatestJuly 21, 2026

Fixed — Preload wedged by a stale object-cache lock

If a persistent object cache drops a delete_option() cache invalidation (WordPress removes the DB row regardless), the lock survives only in the cache and every add_option() existence check fails forever — the run stalls with the lock timestamp frozen until manual intervention (confirmed in production on a Redis-backed site). The stale-lock recovery now also purges the raw cache key and, if add_option() still fails, steals the lock via update_option() (safe: only past the staleness test, so the previous owner is provably dead).

1.4.1

July 21, 2026

Changed — Cloud render accuracy (interaction-aware used CSS)

The cloud render service now measures pages the way a visitor experiences them: after load it auto-scrolls the full page (triggering lazy sections and scroll-reveal states), hovers nav/menu links, and clicks menu toggles behind a navigation-blocking interceptor — all budget-capped so a slow site never overruns its render deadline. A state-pseudo keep-pass additionally retains every :hover/:focus/:checked/::before-class rule even when coverage never saw it fire, and mobile renders now self-identify as bot requests. Menu dropdowns, mobile toggles, and scroll animations no longer depend on the deferred fallback to look right.

Changed — Mobile gets its own optimization, by default

mobile_cache now defaults to ON (existing saved settings are untouched), and critical CSS cache files are keyed per device variant (sol_cc_<hash>-0/1.css), so phones and desktops each get their own page cache, used CSS, and critical CSS — no more desktop-measured CSS served to phones.

Changed — Cloud used CSS always keeps its safety net

When used CSS came from the cloud, the deferred-original fallback is now forced (filterable via sol_rucss_force_fallback_cloud), stylesheets that failed to load during the render are never removed, and renders the service flags as degraded are not applied at all — the page stays on its original CSS instead of risking a half-measured result.

Changed — Edge cache: targeted Woo purges, device-aware keys, free-plan maxing

WooCommerce stock/order events now purge only the affected product and archive URLs at Cloudflare (the last purge-everything path is gone). The HTML cache rule varies by device type (cache_by_device_type, free on all plans) with admin warnings for cookie-switched language setups; purges cover www/apex and http/https variants and chunk properly past Cloudflare's 30-URL call limit; deployed Edge TTL is clamped to the free plan's 2-hour minimum with a 4-hour default; the asset rule gains cache_deception_armor and ignore_query_strings_order; subfolder installs get correctly-prefixed exclusion paths; and a new opt-in toggle enables Cloudflare Tiered Cache (free) with conflict warnings for Rocket Loader, Polish/Mirage, and Flexible SSL.

Fixed — Object cache correctness

incr/decr can no longer resurrect an evicted counter or write a stale runtime value back over a fresher one (missing keys return false, never get recreated; non-atomic fallbacks re-read from Redis, never from the request cache). Predis is now genuinely bundled for hosts without the phpredis extension — with atomic Lua counters on both drivers — the compression threshold is floored so counters stay atomic, mid-request failures record their reason for the status UI, and the Object Cache screen now warns when Redis runs a noeviction or volatile-* maxmemory policy.

Changed — Uninstall moved to the uninstall hooks

The standalone uninstall.php is gone: the complete cleanup (wp-config block stripping, marker-guarded drop-in removal, cache/settings directories, every option, transient, cron hook, table, and plugin meta row, multisite-aware) now runs through register_uninstall_hook() and Freemius' after_uninstall action — one idempotent, best-effort routine that can never turn a plugin delete into a "critical error".

Fixed — Preloader now warms the mobile variant too

With mobile_cache enabled (the 1.4.0 default), the preloader only sent a desktop UA, so mobile visitors hit cold per-device variants (full PHP renders — measured as multi-second mobile TTFB while desktop was ~50 ms). Each queued URL is now requested with both a desktop and a mobile UA in the same parallel waves; a URL still counts once toward progress, a failed mobile variant never consumes a retry (recorded as a soft note), and behavior is byte-identical when mobile_cache is off.

Added — Automatic per-page LCP detection

Every cloud render now records the page's real LCP image into a per-URL auto map (capped at 500 entries, canonical-URL keyed so www/scheme/query variants collapse), and the LCP optimizer consults it on each page: manual URLs first, then the cloud-recorded image for that exact page, then local heuristics. The Autopilot apply path no longer appends detected URLs into the manual lcp_manual_urls textarea — the manual box is purely user-owned — and entries are invalidated with the page's cache. The Images settings screen shows the auto-detected entries with a one-click clear.

Fixed — Preloader self-drive through proxies/CDNs

The preloader's continuation loopback used a 10 ms fire-and-forget timeout that only works when the loopback hits the server directly; behind a CDN/TLS proxy the dispatch was aborted mid-handshake, the self-driving chain died every window, and preloading only advanced while wp-admin was open (or when WP-Cron happened to fire). The kick now allows 5 s for the dispatch (still fully non-blocking), the tick URL is filterable (sol_preload_tick_url) for hosts that need a direct origin, a sol_minutely recurring cron provides a real fallback cadence for the whole run, and the dashboard warns when loopbacks aren't landing — so a run keeps moving even when the host blocks loopbacks entirely. The queue's speed readout now shows the true long-run rate (done/elapsed) instead of an optimistic in-window sample.

Fixed — Admin CSS no longer leaks into WordPress chrome

The React admin bundle's Tailwind preflight (*/html resets) was enqueued globally on SOL admin pages and mangled core menu icons and other plugins' menu styling. The entire built stylesheet is now selector-scoped to #sol-react-root at build time (postcss-prefix-selector), and the classic admin stylesheet's :root design tokens and .is-hidden helper are scoped to the plugin's own containers. wp-admin chrome is no longer touched when an SOL page is open.

Fixed — Preloader progress display and a queue-draining exception path

The dashboard donut could flash NaN% while preloading: the legacy batch poll returned only the raw run status and three SPA call sites stored it as the coverage chart, so cached was undefined (the 3s poll clobbered the real chart between 4s stats polls). The batch payload now carries the real chart and the SPA is NaN-proofed. Separately, a single exception mid-chunk in process_batch() silently dropped the rest of the chunk from the queue — URLs vanished uncounted and done stalled at 0; unprocessed URLs are now returned to the queue and the throwing URL is recorded as a bounded failed attempt. The admin-bar "Preloading X%" badge now uses the same disk-backed coverage numbers as the dashboard (no more stuck 0%), and the coverage badge stays neutral instead of red when there's simply nothing cached yet.

1.4.0

July 21, 2026

Added — Sitemap-based cache preloading

The preloader now builds its warmup queue from XML sitemaps — Yoast SEO, Rank Math, WordPress core sitemaps, a custom sitemap URL, or any sitemap declared in robots.txt — instead of only scanning the posts tables, so taxonomy, author, and date archives get warmed too. Sitemap fetches are XXE-safe, capped (depth 2, 200 files, 10,000 URLs), transient-cached for an hour, and restricted to the site's own host; URLs that the cache exclusion rules would never store are filtered out before queueing. When no sitemap is reachable the preloader silently falls back to the previous content scan, and the WP-CLI wp sol preload output now reports which URL source was used.

Added — Per-path cache TTL rules

A new cache_ttl_rules setting (up to 20 rules of pattern + ttl_hours, wildcard * supported, most-specific/longest match wins) gives individual URL paths their own cache lifespan — e.g. /blog/* at 1 hour, /docs/* at 1 month — while unmatched URLs keep the global TTL. Rules are baked into the early-boot settings file, so both pre-boot delivery and the cron garbage collector honor them.

Added — Logged-in user cache

A new user_cache setting (off by default) serves and writes per-user cached pages for logged-in visitors, keyed by the username from the auth cookie so two users never share a file. Session-sensitive cookies (WooCommerce, EDD, comment/password cookies, and any user-configured exclusions) still hard-bypass the cache. Per-user files are purged on profile update, password reset, and account deletion without touching other users, and Vary: Cookie is emitted while the feature is enabled. Filterable via sol_user_cache_enabled.

Added — CDN URL rewriting

New SOL_CDN module: point a CDN hostname (BunnyCDN, CloudFront, KeyCDN, …) at the origin and asset URLs in the final HTML — uploads, theme/plugin assets, wp-includes, and/or CSS/JS files, each toggleable — are rewritten to the CDN, with per-line exclusion patterns. Rewriting runs on the engine's sol_page_contents_before_output filter after all other optimizers and before the cache write, so cached pages contain CDN URLs and cache hits are never double-processed.

Added — Kill Bloat toggles

Three new switches (all off by default): disable XML-RPC (xmlrpc_enabled filter plus X-Pingback header removal), disable RSS/Atom feeds (head links removed, feed requests get a 403 with a filterable message), and remove jQuery Migrate on the front end (dependency stripped rather than deregistered, so scripts declaring it can't fatal).

Changed — WooCommerce orders purge only what they affect

woocommerce_new_order / woocommerce_checkout_order_processed no longer clear the entire site cache (and Cloudflare edge) on every sale. The new targeted purge walks each order's products through the existing per-post invalidation map — product page, shop page, product category/tag archives — and purges only those URLs, locally and at the edge. sol_woo_purge_urls filter for extensions; sol_woo_order_full_cache_purge restores the old behavior.

Changed — FOUC hardening for Critical CSS

Critical CSS is now placed as early as legally possible in <head> (before every external stylesheet), a static no-hide failsafe flips any deferred sheet still stuck at media="print" to media="all" shortly after window-load (also shipped on RUCSS pages for their deferred fallback sheets), critical cache files carry a stylesheet fingerprint sidecar so a page never pairs with a previous design's critical block, and font-display: swap injection moved to the inject choke point so legacy and cloud-rewarmed files are covered too. Pages without critical CSS remain fully render-blocking — no blank-page gates anywhere.

Changed — Page-cache drop-in self-heal is now visible

When requirements_check() rebuilds a missing advanced-cache.php (e.g. after a server or path migration), a dismissible admin notice reports that caching was briefly inactive and is running again, instead of the drop-in silently vanishing.

Security

  • The temporary wp-config backup written while toggling the WP_CACHE constant now lives

in the system temp directory (with a guarded fallback) instead of the web root, closing a brief window where database credentials could be served as plain text.

  • Cloud render service (api.speedoflight.pro): closed a WebRTC/UDP bypass around the

SSRF guard, hardened client-IP handling for the token rate limiter, added URL-length and stylesheet-input caps, fixed a failed-navigation case that could return an "empty but successful" render plan, rotated the shared secret, and expanded the test suite (111 tests green).

Added — Non-destructive image optimization with restore

Image conversion no longer deletes the original file: JPEG/PNG sources stay in the uploads directory by default, and every conversion writes a _sol_converted record (original path, URL, metadata snapshot, generated files, content URL rewrites, byte counts) so the plugin knows exactly what it changed. Converted images can be reverted — per attachment via a new "Restore original" row action in the Media Library, or in bulk via "Restore all originals" on the Bulk Compression screen — which re-points the attachment and content URLs at the original file and deletes the generated WebP/AVIF versions. A new "Delete originals after conversion" setting (off by default) restores the old space-saving behavior at the cost of revertibility. Conversion output is validated with getimagesize() and the reported format now reflects what the encoder actually produced rather than the requested format; content URLs are only rewritten when the target file provably exists, and all file deletes are realpath-confined to the uploads directory.

1.3.11

July 6, 2026

Fixed — Distribution packaging

The plugin now installs into the standard speed-of-light/ folder (its Freemius slug) instead of a space-separated Speed Of Light Pro/ directory, matching WordPress slug conventions and avoiding space-in-path issues on some servers and CDNs. The release archive also no longer carries internal development artifacts (agent tooling, CI notes, dependency CLI binaries, VCS metadata) — only the files the plugin needs at runtime.

Verified — Compatibility gauntlet

Full install → activate → deactivate → delete → uninstall lifecycle confirmed clean on a disposable WordPress (no fatal on removal). Front-end optimization verified non-destructive alongside WooCommerce (jQuery and add-to-cart scripts left functional; cart, checkout and My Account correctly excluded from page caching) and Elementor (coexistence, zero fatals). No code behavior changes from 1.3.10.

1.3.10

July 3, 2026

Added — True Image Sizing

Browsers over-download images when the theme's sizes attribute doesn't match how big the image is actually shown — so a picture displayed at 200px can pull a 1200px file. Speed of Light now corrects this using something a rules-based plugin structurally can't do: it compares each image's real rendered width at both the mobile and desktop renders. If the width is the same at both (a fixed-size image — a logo, an icon, a thumbnail), it pins sizes to the measured width so the browser picks the smallest file that still looks sharp. If the width scales with the viewport (a fluid or hero image), it's left completely alone — pinning it would blur it on wide screens. Only images that are genuinely oversized, carry a srcset, and are proven fixed-size across both viewports are touched; the LCP image and CSS backgrounds are never touched. Reclaims image bytes competitors can't compute because they never measure the render. Automatic on cloud-accelerated sites once both viewports have been analyzed; can be disabled with the sol_true_image_sizing filter.

1.3.9

July 3, 2026

Pre-launch hardening and cleanup pass. A full audit of the cloud/performance surface confirmed the render pipeline, plan application, Autopilot apply/undo, license-gating, and API contract are complete and correct — this release prunes the dead scaffolding the audit surfaced and hardens one operational edge.

Removed — dead legacy code (no behavior change)

  • Removed the superseded per-endpoint cloud CSS path (SOL_Cloud_Client::generate_used_css(),

generate_critical_css(), and their private request() helper). Everything has gone through the unified single-render /v1/analyze path since 1.2.x; these had zero call sites.

  • Removed two unused SOL_Cloud_Plan accessors (render_blocking(), priority_graph()) that

no optimizer consumed.

  • Removed an unused admin-UI component (GlobalSearch) that was never mounted.

Cloud engine (paired, API-side)

  • The render service's soft deadline (ANALYZE_BUDGET_MS) is now clamped in code to always sit

below the hard request timeout, so no environment configuration can reintroduce the case where a tuned-down hard timeout made slow-but-valid sites hard-fail instead of returning a best-effort partial plan.

1.3.8

July 2, 2026

Fixed — Redis fell back to memory even though TCP worked

  • The drop-in's set-and-forget socket probe (prefer a local Unix socket when the config is

at localhost defaults) could take the whole connection down: modern phpredis (v6+) THROWS on a failed connect where v5 returned false, so a Redis socket file that exists but isn't connectable (typical case: root-owned redis.sock the PHP user can't open) aborted the chain before the TCP fallback ever ran. Meanwhile "Test Connection" goes straight to TCP and reported success — Redis reachable, plugin stuck in memory mode. Every connection attempt is now isolated: a failed socket probe (returned false OR threw) falls through to TLS/TCP exactly as the comment always promised.

  • AUTH results are now verified (wrong password previously sailed through to a confusing

PING failure) and failures are distinguished from unreachability.

  • The status line now tells you WHY it's in memory mode: "authentication failed — check the

Redis password", "tcp connect failed: Connection refused", "no PING response" — instead of the generic "Redis unreachable". Reason also appears in the diagnostics payload.

  • Verified against the real drop-in with both phpredis 6 (throwing) and phpredis 5

(false-returning) semantics: dead socket + live TCP now connects via TCP in both.

  • The drop-in updates automatically when the plugin updates (existing behavior) — no manual

action needed beyond installing this version.

1.3.7

July 2, 2026

Changed — Insights redesigned for non-technical users

The Insights tab now leads with a plain-English verdict and ONE button; everything technical is behind an opt-in "Show technical details" toggle (remembered per browser).

  • Verdict hero: "Your site is in great shape" / "We can speed up your site right now —

N safe improvements ready" / "Almost perfect — a few things need you", with a single primary action ("Fix everything for me" or "Check my site"). No severity taxonomy, no metric names, no culprit URLs in the default view.

  • Autopilot is finally visible: an on/off switch with honest copy ("nothing changes

without you" when off — stored as an explicit choice that survives the default), a plain-English list of what the last run applied ("Font optimization", "Page caching"…), and one-click Undo that restores the previous settings from the snapshot every apply already records, purges caches, and is guaranteed not to be silently re-applied (the convergence guard already treats the undone set as settled).

  • "Needs a quick look from you": findings the plugin can't auto-fix, deduplicated across

pages (the same oversized image on ten pages is one card), worst-first, one sentence each, with the step-by-step how-to behind a "Show me how to fix it" link.

  • The full technical view (severity tiles, per-page groups, filters, pagination, Fix/Verify

buttons) is unchanged under the details toggle.

  • New REST endpoints: POST /insights/autopilot (switch), POST /insights/undo (restore

snapshot; refuses to write anything outside the plugin's own sol_* option groups).

1.3.6

July 2, 2026

Fixed — excluded means 100% excluded

  • 1.3.5 stopped cloud analysis and used-CSS rewriting on excluded pages, but the rest of the

optimizer pipeline — JS defer/delay, minify, critical CSS, fonts, lazy-load, facades, LCP, injected scripts — still ran there, which could break dynamic pages (e.g. forms whose scripts were deferred/delayed despite the page being excluded).

  • Exclusions tab rules (post IDs, post types, page paths, query strings) now switch off the

ENTIRE optimization pipeline for matching pages at the single master gate every optimizer runs through, plus a per-tag re-check in the script filter so post-ID exclusions (only knowable after the main query) are honored for defer/delay too. An excluded page is served exactly as the theme renders it: not cached, not preloaded, not analyzed, no HTML rewriting, no script modification, no injected beacons.

  • One shared matcher for all layers (engine, assets, cloud client, RUCSS, preloader) — the

answer to "is this page excluded?" can no longer differ by subsystem.

  • After updating: if a page was cached before you excluded it, purge once so the old

optimized copy is gone.

1.3.5

July 2, 2026

Fixed — "leave this page alone" now means the whole pipeline

An audit of how user exclusions interact with the cloud pipeline found two gaps; both fixed.

  • Excluded URLs are no longer analyzed or CSS-optimized. The cache URL exclusions

(post IDs, post types, page paths, query strings) were honored by the disk cache and the preloader, but an organic visit to an excluded page could still queue it for cloud analysis, and used-CSS rewriting still ran on it. The cloud client and the used-CSS pipeline now check the same exclusion rule set the preloader uses (single shared matcher), so an excluded page is untouched end to end: not cached, not preloaded, not analyzed, not rewritten.

  • User JS exclusions now always beat cloud recommendations. Cloud-recommended defer

candidates were matched before the user's "exclude from defer" lists, so excluding a script the cloud had flagged did nothing. Exclusions are now tested first — for both handles and sources — making the user's exclusion absolute (matching how JS delay already behaved).

  • Password-protected pages are no longer sent for cloud analysis (an anonymous render only

ever sees the password form).

1.3.4

July 2, 2026

Changed — Asset Versioning moved to the Edge Cache tab

  • The Asset Versioning card (versioning toggle, cache headers toggle, and the assets-only

Cloudflare rule deploy/remove buttons) now lives on the Edge Cache tab, next to the Cloudflare wizard its Deploy button depends on — the rule can't deploy without a connected Edge account, so the controls belong together. Settings keys are unchanged; nothing to reconfigure. Both toggles also gained plain-language descriptions (the browser-cache-header benefit works even without Cloudflare).

1.3.3

July 2, 2026

Fixed — Performance Insights fixes now visibly work

  • Scans warm the cache first. A scan right after install, purge, or applying a fix hit a

cold origin, so the cloud measured raw WordPress generation time (~3s) instead of what visitors get from the cache (~ms) — and reported a Critical "slow TTFB — enable page caching" on sites where caching was already on. Clicking Fix it then purged and re-scanned cold, regenerating the same finding forever. Scans now prime the URL through the normal front-end path (two anonymous GETs) before the cloud render, so every scan measures the steady state real visitors experience.

  • Honest buttons. When a finding's fix is already enabled, the button now reads

"Verify fix" with an explanation (re-checks the page: purge, warm, re-scan) instead of a "Fix it" that appears to do nothing.

  • "Fix all safe issues" is now a clearly-labeled primary button next to Re-scan

(previously a ghost-styled "Re-apply safe fixes" that read like a maintenance action).

Fixed — stale "Connect Cloudflare" notice

  • The dashboard's environment notices come from a profile cached for 12 hours, and nothing

refreshed it when the facts changed — so after connecting Cloudflare the "Cloudflare appears to be in front of your site. Connect it in Edge Cache…" banner kept showing until the cache expired. The profile now recomputes immediately when Cloudflare credentials are saved and when plugins are activated/deactivated.

Notes

  • Insights scanning is (and was) front-end only: the manual Re-scan analyzes the homepage,

and background analysis is gated to the front page, blog home, and single posts/pages — never wp-admin, archives, or feeds.

1.3.2

July 2, 2026

Fixed — Edge Cache wizard

  • With Cloudflare credentials saved but no site selected yet, the wizard parked on the

Account step until the user discovered the "Re-list Cloudflare sites" button — even though the site list had already loaded silently in the background. The wizard now advances to the Site step automatically as soon as the zone list arrives. One-shot: an explicit "Back" click sticks, and the auto-advance never fires while the replace-credentials form is open.

1.3.1

July 2, 2026

Automatic Google Fonts optimization now actually lands on every site — two long-standing gaps meant most sites kept a render-blocking fonts.googleapis.com request (and its follow-on font CSS chain) even with self-hosting enabled. On a reference site this was the entire gap between a mobile PageSpeed score of 76 and the low 90s. No new settings: everything below is automatic and fail-open (any failure leaves the site's original CSS untouched).

Fixed — Google Fonts self-hosting was a silent no-op for properly-enqueued fonts

  • WordPress prints enqueued stylesheet URLs with HTML-encoded ampersands (&). The

fonts optimizer compared a decoded URL against the raw tag, so the local-copy swap never matched: the font CSS was fetched, the font files were downloaded and preloaded — but the render-blocking Google <link> stayed in the page, and the preloaded local files were never referenced (fonts downloaded twice). The rewrite now targets the verbatim attribute value, with the decoded form as fallback for hand-written markup.

  • Font preloads are now only emitted for the local copies when the tag swap actually landed;

otherwise the real gstatic URLs are preloaded instead, so preloads always match what the page loads.

  • The Google Fonts @import scanner now tolerates entity-encoded ampersands.

Fixed — optimized CSS no longer ships render-blocking @import chains

  • Used-CSS output (both locally generated and cloud-delivered) preserved @import statements

verbatim, so the "lean" stylesheet still triggered sequential render-blocking fetches — including cross-origin Google Fonts CSS. Imports are now resolved at generation time at the single shared write chokepoint: the imported sheet is fetched (Chrome UA so Google serves modern woff2/unicode-range faces), its url() references are rebased, its @font-face files are self-hosted through the existing fonts pipeline, and the content is inlined — wrapped in the import's media query when it carried one. layer()/supports() imports are left untouched (inlining would change the cascade). Nested imports resolve two levels deep under a hard wall-clock budget; anything unresolvable keeps its original statement.

  • Remote import fetches pass WordPress's SSRF guard including every redirect hop

(reject_unsafe_urls), are size-capped, and cache positive/negative results.

  • The used-CSS file path now also strips scripted-CSS vectors (javascript: imports,

expression()), mirroring the sanitizer the inline critical-CSS path already had.

  • Self-hosted font files from hosts other than fonts.gstatic.com get a URL-hash filename

prefix so identical basenames from different origins can never collide in the cache.

Cloud engine (API 1.29.0, paired with this release)

  • The render service now resolves @import in its used-CSS assembly the same way (DNS-pinned

SSRF-safe fetches, Chrome UA, media-query wrapping, depth-limited, fail-open) and drops the duplicate coverage entry Chrome reports for an inlined sheet — which also fixes desktop bundles carrying degraded TTF-flavor Google Fonts CSS fetched under the render bot UA.

  • Critical CSS no longer silently drops @import-ed same-origin stylesheets: the collector

now recurses into imported sheets so their above-the-fold rules and @font-face reach the inline critical CSS.

1.3.0

July 1, 2026

The consolidated launch build. In addition to the Global Purge Menu restoration below, 1.3.0 rolls up a broad reliability, security, performance, and compatibility remediation pass across the caching engine, the cloud Core Web Vitals pipeline, the Redis object cache, and the admin UI. The full feature set at this release is: three-layer caching (dynamic origin render → local disk cache served pre-WordPress → Cloudflare edge full-page HTML caching), a native Redis object-cache drop-in with a GUI and WP-CLI, cloud-powered real-Chrome Core Web Vitals optimization (used-CSS, critical-CSS, LCP, image, CLS, font, and JS plans from a single render), Performance Insights (a cloud "detective" that diagnoses per-page problems, grouped by content type and paginated), Autopilot (plug-and-play automatic application of the cloud's approved safe fixes), real-user monitoring (RUM) field Core Web Vitals, Smart Configuration, image optimization (WebP/AVIF), self-driving preloading, and the global purge menu.

Fixed — Global Purge Menu

  • Restored the full global purge dropdown behavior from the older admin UI.
  • The header now loads object-cache status at app startup, so the purge dropdown can show Object cache and Everything actions on Dashboard, Local Queue, Cached URLs, and other tabs without requiring the user to visit the Object Cache page first.
  • The Object Cache page now publishes its status into shared app state after load/enable/disable/flush so the header updates immediately.
  • Object cache flush now returns a fresh object-cache status payload from the REST API.
  • The menu still falls back to the single safe Cached pages action when no persistent object cache is active.

Security

  • Hardened handling of cloud-delivered CSS so optimized stylesheets returned from the render service are validated and sanitized before being written and served.
  • Hardened internal request handling on the plugin's own endpoints and background/loopback calls.
  • Reaffirmed that Cloudflare credentials are never exposed to the browser and are stripped from settings exports; Redis passwords stay in a private, owner-only config file and are never written into public wp-config blocks.

Reliability & correctness

  • Cached HTML (and its pre-compressed gzip/Brotli variants) is now correctly served when Cloudflare Edge caching is enabled, instead of being bypassed.
  • CSS optimization is more reliable: asset URLs inside optimized CSS are rewritten correctly (fonts, icons, background images, and url() references resolve from the new stylesheet location), and minification is safer — modern CSS selectors and values are preserved rather than being mangled.
  • Optimized CSS is now invalidated automatically when the active theme, any plugin, or WordPress core is updated, so a stale used-/critical-CSS bundle can't outlive the markup it was built from.
  • JavaScript deferral no longer touches jQuery or WordPress core libraries, and script exclusion rules now match by script handle or URL exactly as documented.
  • Object cache never fails a page render when a single value can't be cached — an uncacheable/oversized value degrades to a clean miss instead of surfacing an error; atomic increment/decrement counters now preserve the key's existing expiry.
  • Edge cache correctness: unpublished and trashed content is purged from the Cloudflare edge; local-cache URL exclusions are honored at the edge; and the assets-only rule and the HTML rule are managed independently so toggling one no longer disturbs the other.
  • Admin UI reliability: settings saves now surface real error handling, and cache/optimization actions report honest success or failure instead of always reporting success.

Performance

  • Per-device caching: mobile visitors are served mobile-optimized HTML from a separate cache variant rather than the desktop render.
  • More accurate mobile Core Web Vitals: cloud renders now use real throttled mobile emulation so mobile diagnostics and LCP/CLS measurements reflect a mobile device.
  • Better font optimization: proper woff2 subsets, cached and non-render-blocking font fetching, and correct font preloading.
  • Improved real-user INP (interaction) measurement for more accurate field data.

Compatibility

  • Restored PHP 7.4+ compatibility. A bundled third-party library had silently begun requiring PHP 8.1; the dependency is pinned/patched so the plugin runs on PHP 7.4 and up again, matching the documented Requires PHP: 7.4.

Autopilot

  • Autopilot is now safe and convergent: it applies the cloud's approved safe fixes once, then settles — it no longer loops re-applying the same actions or wipes caches unnecessarily.
  • Continues to respect user overrides (a setting the user has changed is left alone) and only applies actions the cloud safety gate has explicitly approved.

1.2.9

July 2, 2026

Fixed — WordPress Plugin Deletion

  • Added a standalone WordPress-native uninstall.php so deleting the plugin no longer depends on the full Speed of Light bootstrap or autoloaded classes being available during WordPress' plugin-delete request.
  • The uninstall file is dependency-free, best-effort, and guarded with Throwable catches so a cleanup edge case cannot turn an otherwise successful filesystem delete into the admin-screen "Deletion failed: There has been a critical error on this website" notice.
  • Removes only clearly Speed of Light-owned drop-ins:
    • wp-content/advanced-cache.php when it contains Speed of Light markers;
    • wp-content/object-cache.php when it contains the SOL object-cache marker/header;
    • wp-content/sol-object-cache-config.php.
  • Removes the Speed of Light-managed WP_CACHE wp-config block and the Speed of Light Object Cache SOL_REDIS_* wp-config block without touching host/user-managed cache constants.
  • Cleans Speed of Light cache/settings directories, options, site options, transients, site transients, scheduled hooks, RUM/RUCSS/log tables, user avatar metadata, and plugin-specific transient rows.
  • Handles multisite by iterating sites when WordPress multisite helpers are available, while still allowing single-site deletion to finish cleanly if any cleanup step is unavailable.

1.2.8

July 1, 2026

Added — Plug-and-Play Autopilot

  • Performance Insights scans now automatically apply Cloud API-approved safe fixes after the scan completes.
  • Background cloud findings now schedule an Autopilot worker so safe fixes can be applied without requiring the user to open the Insights screen.
  • The background worker uses a lock and duplicate-action cooldown to avoid repeated setting writes from recurring scans.
  • No user-facing optimization mode selector was added; the API decides what is safe, and the plugin applies only those approved actions.
  • Added a settings snapshot before Autopilot changes. The snapshot is stored in sol_insight_last_autopilot_snapshot and records the previous option values, applied actions, patch, timestamp, and source.
  • The scan response now includes an autopilot block showing what was applied or why nothing safe was applied.
  • The admin toast now tells the user when safe fixes were applied automatically.
  • Manual single-fix and fix-all paths now reuse the same internal apply helper as Autopilot, keeping sanitization, cache purging, preload, image jobs, and rescans on one code path.

Added — Cloud API Autopilot Plan

  • Cloud API 1.27.0 adds a top-level autopilot diagnostics object.
  • The API now emits:
    • approved_actions;
    • approved_action_count;
    • skipped findings/actions with reasons;
    • blocked actions;
    • cautious actions;
    • safe actions;
    • per-action confidence;
    • estimated savings.
  • Autopilot approval requires the finding to be auto-fixable, mapped to a plugin action, not blocked/cautioned by safety gates, and high enough confidence for automatic application.
  • Added tests proving safe static asset cache fixes are approved while blocked checkout/defer scenarios are skipped with a safety reason.

1.2.7

July 1, 2026

Session Summary — Production Hardening, Cloud API Intelligence, and Packaging

  • Completed a broad production-readiness pass across the Speed of Light (Pro) WordPress plugin and the private Speed of Light Cloud API.
  • Audited the plugin behavior against live-site concerns raised during testing: Performance Insights apply behavior, Cloud API visibility, Cloudflare/edge-cache toggles, object cache safety, uninstall/delete behavior, package size, dead-code risk, and API survivability after server reboot.
  • Rebuilt and verified the production test package at /Users/leo/Desktop/speed-of-light-pro-production-test.zip.
  • Deployed the Cloud API to api.speedoflight.pro and verified live health after restart.
  • Added a full next-agent handoff document at SESSION_HANDOFF_2026-07-01.md with exact paths, commands, deployed state, safety rationale, intentional non-automation decisions, validation output, and recommended next work.

Added — Cloud API Static Cache Policy Intelligence

  • Added Cloud API 1.26.0.
  • Added a new real-browser static cache policy detector to the API.
  • The API now inspects actual response headers for first-party static resources captured during Playwright renders.
  • Detects first-party CSS, JavaScript, fonts, and images that are versioned or cache-safe candidates but are served with:
    • no-cache
    • no-store
    • private
    • must-revalidate
    • missing browser TTL
    • very short max-age
  • Excludes third-party assets from auto-fix decisions because their cache headers are not safely controlled by the WordPress plugin.
  • Emits a new cache.static-policy-* Performance Insights finding when static asset cache policy is wasting repeat-visit/CDN performance.
  • Adds estimated repeat-visit byte savings from cache-hostile first-party static assets.
  • Adds a new static_cache_profile inside the API safety payload so WordPress can store the detailed reasoning, not just the finding card.
  • Added unit tests proving:
    • first-party bad-cache assets are detected;
    • third-party bad-cache assets are ignored for plugin-controlled fixes;
    • the profile exposes affected count and affected bytes;
    • the finding maps to the safe plugin action.

Added — Cloud API JavaScript Coverage Intelligence

  • Added real Chrome JavaScript coverage analysis to the Cloud API.
  • The API now consumes Playwright JavaScript coverage output and builds a js_coverage_profile.
  • The profile calculates:
    • total JavaScript bytes observed;
    • unused JavaScript bytes;
    • unused ratio;
    • candidate scripts for safe delay/defer;
    • protected scripts that must not be delayed/deferred;
    • hazards that make automation risky.
  • Added conservative protection for WordPress and commerce-critical scripts, including:
    • WordPress core runtime;
    • jQuery;
    • WooCommerce;
    • cart/checkout/account/login surfaces;
    • nonce and admin-ajax.php flows;
    • REST/API-dependent frontends;
    • forms, payment, CAPTCHA, builders, sliders, and interaction runtimes.
  • Added safe coverage-derived delay/defer patterns for clearly non-critical analytics/media/vendor scripts.
  • Merged coverage-derived safe patterns and exclusions into the API safety profile.
  • Added tests proving GTM-style scripts can become safe delay candidates while jQuery remains protected.

Added — Broader Proprietary Cloud Safety Profiles

  • Expanded the Cloud API into a multi-profile diagnostic system designed to compete with premium optimization products while staying conservative for varied WordPress installs.
  • Profiles added or connected during the session include:
    • compatibility profile;
    • modern WordPress runtime profile;
    • dynamic surface profile;
    • personalization/session profile;
    • cache freshness profile;
    • responsive surface profile;
    • viewport coverage profile;
    • field data/RUM profile;
    • priority graph profile;
    • font profile;
    • monetization/ad-stack profile;
    • embed profile;
    • JavaScript dependency profile;
    • JavaScript coverage profile;
    • breakage guard profile;
    • static cache policy profile.
  • These profiles feed the API's one-click-fix gate so risky optimizations are blocked or downgraded before the plugin applies them.

Added — Performance Insights Apply Fixes Integration

  • Added WordPress-side support for storing the API's richer safety payload per analyzed page.
  • Added sanitizer support for the new static_cache_profile.
  • Added sanitizer support for the new js_coverage_profile.
  • Connected API-provided safe delay/defer patterns to plugin settings during Apply Fixes.
  • Connected API-provided delay/defer exclusion patterns to plugin settings during Apply Fixes.
  • Added the new safe static_asset_cache action.
  • static_asset_cache enables:
    • SOL asset versioning;
    • long-lived cache headers for versioned first-party CSS/JS.
  • Kept Cloudflare static asset rule deployment as an explicit user action instead of hiding it behind Apply Fixes, because deploying/removing Cloudflare rules mutates the customer's Cloudflare account.

Added — Image Optimization From Insights

  • Connected cloud-detected oversized image findings to the local compression pipeline.
  • image_resize_serve findings now seed the local queue with the cloud-detected oversized image candidates.
  • Apply Fixes can now immediately start local compression work for oversized images found by the API.
  • Limited byte-changing compression behavior to the image resize/serve fix path so dimension-only or priority-only fixes do not unexpectedly rewrite media.

Changed — Performance Insights Scope and Behavior

  • Narrowed automatic cloud insight collection to user-tunable page types rather than letting archives/authors/tags fan out into repeated asset findings.
  • Focused insight analysis on front page, blog home, and singular content where performance actions are more actionable.
  • Reduced duplicate/noisy findings from archive pages.
  • Updated Performance Insights language so the UI more honestly says the cloud highlights biggest slowdowns and offers one-click fixes where safe.
  • Clarified that not every finding is auto-fixable; some are deliberately guidance-only when fixing would be content/theme/plugin-specific.

Changed — Cloud API Health and Deployment

  • Verified api.speedoflight.pro is served by the Speed of Light Cloud API process with Playwright/Chromium available.
  • Confirmed live health endpoint returns:
    • ok: true;
    • current API version;
    • browser available;
    • proxy available.
  • Verified the API process is managed by sol-api.service.
  • Verified the service is enabled under systemd so it is expected to survive server reboot.
  • Deployed API updates with timestamped server backups.
  • Ran remote API syntax checks and tests before restart.
  • Restarted the API process and verified public health after deployment.

Fixed — Insights Apply Fixes Did Not Fully Apply JS Changes

  • Fixed Apply Fixes so JS defer/delay actions switch asset optimization into custom mode.
  • This ensures cloud-measured safe defer/delay source patterns and exclusions are honored by the frontend optimizer.
  • Prevents the UI from claiming a fix was applied while the runtime mode still ignores the specific cloud-provided rules.

Fixed — LCP Optimization From Cloud Insights

  • Connected the API's exact real-browser LCP preload URL into the local LCP optimizer manual override list.
  • Improves handling for:
    • background-image heroes;
    • lazy-loaded heroes;
    • late-discovered images;
    • cross-origin LCP resources;
    • hero images with CDN/query-string URLs.

Fixed — Breakage Guard for One-Click Fixes

  • Added a real-browser breakage guard that checks runtime errors, failed scripts/styles, stateful forms, event-heavy UI, menus, overlays, carousels, and builder-style frontends.
  • Blocks risky one-click delay/defer fixes when the page looks fragile.
  • Seeds safer exclusion lists instead of blindly delaying/defering scripts.

Added — Font Optimization Intelligence

  • Added API-side detection for above-fold font usage.
  • Detects Google Fonts chains and WOFF2 preload opportunities.
  • Adds font preconnect origins and preload candidates into the safety payload.
  • Preserves icon-font and layout-sensitive font safety by feeding exclusions/safelists where needed.
  • Maps safe font findings to local font optimization settings.

Added — Embed and Third-Party Optimization Intelligence

  • Added detection for heavy YouTube, Vimeo, Maps, social, and eager iframe embeds.
  • Maps safe embed findings to lightweight facades, iframe lazy loading, image/dimension helpers, and provider preconnects.
  • Added third-party pressure analysis using real browser bytes and long tasks.
  • Allows measured delay/self-hosting recommendations only for vendors considered safe or delayable.
  • Protects monetization/ad stacks, including AdSense, Google Publisher Tag, header bidding, video ads, affiliate scripts, and related revenue-critical runtimes.

Added — Dynamic, Personalized, and Freshness-Sensitive Route Protection

  • Added detection for WooCommerce/session endpoints, AJAX/REST calls, nonce forms, login/account/cart/checkout/search/preview routes, and other dynamic WordPress surfaces.
  • Added personalization/session-variant detection for language, currency, membership, wishlist, compare, consent, shipping, and location signals.
  • Added cache freshness detection for feeds, sitemaps, paginated archives, search, filtered listings, REST lists, and events/calendar routes.
  • These signals expand cache, JS, and instant-navigation exclusions so aggressive optimization does not stale or personalize the wrong page.

Added — Responsive and Viewport Safety

  • Added responsive surface detection for mobile menus, overlays, sticky headers, accordions, sliders, and breakpoint-only content.
  • Added viewport coverage signals for responsive CSS, container queries, responsive media, breakpoint-specific DOM, and mobile hero surfaces.
  • These profiles protect mobile/tablet/desktop layouts when applying CSS/JS optimizations.

Added — Field Data and Interaction Pressure

  • Connected anonymous local RUM p75/sample/grade summaries into cloud analysis.
  • API can now tune lab-render recommendations using field pressure for:
    • LCP;
    • INP;
    • CLS;
    • TTFB.
  • Added long-task and BFCache signal handling.
  • Maps safe interaction improvements to the INP optimizer and measured delay recommendations.

Fixed — Cloudflare / Edge Cache Behavior

  • Restored the three-layer cache model:
    • dynamic/origin render;
    • local disk cache;
    • Cloudflare edge cache.
  • Edge cache TTL now follows the configured Edge Cache TTL instead of a hardcoded long value.
  • Added safer stale-while-revalidate handling.
  • Broadened logged-in/cart/comment/store cookie exclusions so private pages are not edge cached.
  • Ensured content changes purge the edge cache.
  • Fixed the Edge Cache toggle so disabling edge cache stops sending edge directives such as CDN cache headers and s-maxage.
  • Restored one-click Deploy/Remove controls for the assets-only Cloudflare cache rule.
  • Clarified that the assets-only Cloudflare rule keeps HTML dynamic and only targets versioned CSS/JS.
  • Removed the old "Bypass CDN during preload" option because it could not reliably reach origin behind all CDN/WAF setups and normal preloading is the safer default.

Fixed — SOL Cache Signature Comment

  • Fixed the SOL verification/comment behavior so the signature can appear reliably in cached HTML.
  • Addressed the reported case where Cloudflare/plugin CDN state made the comment disappear even though local cache files contained optimized HTML.
  • Ensured edge-cache disabling actually removes origin edge directives so Cloudflare does not continue serving stale/incorrectly marked pages because of plugin state.
  • Kept the signature behavior filterable through the existing signature filter.

Fixed — Deactivation/Delete Critical Error

  • Fixed the false WordPress "Deletion failed: critical error" behavior seen when deleting the plugin.
  • Hardened cleanup paths so deactivation/delete can complete without surfacing a fatal to the plugin screen.
  • Preserved cleanup behavior for cache drop-ins and generated artifacts.

Added and Hardened — Object Cache

  • Audited the Redis object cache implementation against Object Cache Pro-style behavior.
  • Hardened the object-cache drop-in against redeclaration/fatal errors.
  • Made object-cache installation more atomic.
  • Added/kept safe degradation so Redis connection problems do not take down the WordPress site.
  • Added support for TCP, Unix socket, and host-defined Redis constants.
  • Added branded SOL_REDIS_* configuration handling.
  • Ensured password handling stays private rather than writing secrets into public config blocks.
  • Added object-cache cleanup on disable/uninstall:
    • remove the drop-in;
    • remove SOL config block;
    • flush SOL-owned keys;
    • avoid leaving stale plugin state.
  • Added object cache diagnostics for connection method, Redis version/mode, memory, hit/miss ratio, operations, uptime, evictions, serializers/codecs, and environment details.
  • Added object cache value compression support using the fastest available codec in order:
    • Zstandard;
    • LZ4;
    • gzip.
  • Compression only keeps compressed values when smaller and only targets values large enough to benefit.
  • Added object-cache UI/UX improvements including live connection method, dashboard stat consistency, charts, and toolbar flush action.
  • Fixed a stale active-plugins/object-cache edge case where enabling object cache could make the plugin appear deactivated until refresh/reactivation.

Changed — Admin UI and Health Checks

  • Improved Cloud API health wording so successful API connection is clearer.
  • Avoided reporting the API as degraded because of one transient auth hiccup when a recent valid token/success exists.
  • Added a clearer Cloud API health section that names the API connection checks without over-explaining internal behavior.
  • Fixed health false negatives including inverted object-cache results, cache MISS confusion on healthy cached sites, NaN percentages, and overly aggressive overall fail state.
  • Polished dashboard and health UI alignment, badges, icon/text alignment, object-cache cards, cache stats, and chart presentation.
  • Ensured admin UI uses current built assets and production packaging excludes dev-only admin UI source and dependencies.

Changed — Package Size and Production Zip

  • Audited the production zip after the package was unexpectedly around 6 MB.
  • Rebuilt the package with development artifacts excluded.
  • Excluded:
    • .git;
    • .gitignore;
    • .codegraph;
    • node_modules;
    • admin UI source files;
    • admin UI package files;
    • .DS_Store;
    • temporary/cache folders;
    • nested zip files.
  • Final production test zip is approximately 2.2 MB.
  • Final package contains 844 files.

Security and Privacy

  • Confirmed Cloudflare credentials are not exposed to the browser.
  • Cloudflare tokens/API keys are stripped from settings exports.
  • Cloudflare credential options are kept non-autoload where applicable.
  • API health output remains intentionally low-detail.
  • Static asset cache auto-fix avoids mutating Cloudflare rules automatically.
  • One-click fixes are gated by the API's safety profiles before the plugin applies settings.

Server/API Operations

  • SSH access to the API host was verified.
  • sol-api.service was checked and confirmed active.
  • systemd enablement was verified so the API should come back after reboot.
  • API deployment flow used:
    • create timestamped backup on server;
    • sync app files excluding node_modules, env files, and git metadata;
    • run node -c server.js;
    • run API test suite remotely;
    • restart API process;
    • verify https://api.speedoflight.pro/health.
  • Latest deployed API health:
    • version 1.26.0;
    • browser true;
    • proxy true;
    • ok true.

Validation

  • API local tests passed: 93 passed, 0 failed.
  • API remote tests passed: 93 passed, 0 failed.
  • Plugin targeted PHP lint passed for changed PHP files.
  • Full plugin PHP lint passed.
  • Production zip was rebuilt and scanned for excluded development paths.
  • Production zip version verified as 1.2.7.
  • Live API health verified after deployment.

Release Artifacts

  • Plugin version: 1.2.7.
  • Cloud API version: 1.26.0.
  • Production test zip: /Users/leo/Desktop/speed-of-light-pro-production-test.zip.
  • Server backup created before latest API deploy: /home/speedoflight-api/htdocs/api.speedoflight.pro.backup-20260701-174611.

1.0.16

June 22, 2026

Added — Preload: bypass CDN + name the blocker

  • "Bypass CDN during preload" toggle (Cache → Local Queue → Preload Settings). When the preloader gets 403/blocked responses (common when a CDN/WAF blocks server-side requests), enable this to preload directly from your origin server (127.0.0.1), skipping Cloudflare/CDN bot-protection and WAF rules entirely. The backend already supported this; it now has a UI.
  • Preload failures now name the blocker. Instead of just "http_403; body N bytes", the diagnostic reports which layer returned the error — e.g. "blocked by Cloudflare (Ray …)", "blocked by ModSecurity (origin WAF)", or "blocked by origin (nginx)" — derived from the response's Server/CF-Ray headers and body, so you can see exactly what to allowlist.

1.0.15

June 22, 2026

Fixed — Admin bundle loading (important for updates)

  • The admin UI now always loads the current React bundle. The loader preferred a legacy dist/.vite/manifest.json, which an overlay (non-clean) update could leave behind from an older install — causing the dashboard to keep loading stale admin JS (so UI fixes appeared "not applied"). It now prefers the shipped dist/manifest.json. If you previously updated by copying files over an old install, this guarantees the latest dashboard loads. (A one-time tip: delete any leftover assets/admin-ui/dist/.vite/ folder on the server.)

1.0.14

June 22, 2026

Fixed — Bug audit (22 issues, adversarially verified)

High

  • Auto DB-optimize / clear-transients no longer run when disabled. schedule_events() was force-scheduling those Kill Bloat cron jobs whenever the page cache was on, overriding the user's toggle; the handlers also lacked an enabled-check. They are now owned solely by Kill Bloat and the handlers respect the toggle. (pre-existing)
  • RUM metrics table is reliably pruned. The prune cron was being cleared on every settings save / when the page cache was toggled off, so the table could grow unbounded; RUM lifecycle is now independent of the page cache.
  • Multilingual edge caching no longer serves the wrong language. When language-aware caching is active, the response now sends Vary: Cookie so a CDN (Cloudflare) doesn't serve one language's HTML to everyone.
  • Third-party self-hosting now actually applies to cached pages. The first time a script is self-hosted, the page cache is purged once so pages re-bake with the local URL (previously the remote URL stayed baked in).
  • Cloudflare edge: static purge_all()/deploy_rules() no longer re-register the edge hooks (which could double-fire purges); the Edge screen no longer flips a configured connection to "Not Set" after saving an unrelated toggle.

Medium

  • LCP preload no longer corrupts image URLs containing $/\ (e.g. signed CDN/query params); <picture> heroes are now marked on the inner <img>, not the wrapper.
  • Warm-mode/translation cache clears skip query-string permalinks so they can't clobber the home cache on plain-permalink sites; the archive-invalidation debounce is now per-post (a second edited post is no longer skipped).
  • Image conversion keeps the original when the converted file isn't smaller (no more negative "Saved %"); Gravatar caching no longer blocks the front-end render (downloads run on cron) and varies the cached file by avatar args.

Low

  • LCP carries the computed sizes/lazy flag through so JS-lazy heroes are actually un-lazied; comment-purge guards a null get_comment(); converter guards a false attachment URL; RUM stores timestamps in GMT to match query windows; hover-preload no longer over-blocks legitimate action= links; preloader fallback writes the deterministic language cache key.

Fixed — Admin UI

  • Settings toggles are correctly aligned in mixed grids. The 1.0.13 alignment change centered description-less toggles, which drifted them out of line with description-bearing neighbours in two-column grids (e.g. the Local Cache tab). Toggle + label now always share one centered row with the description hanging below, so every toggle lines up regardless of its neighbours.

1.0.13

June 22, 2026

Fixed — Admin UI

  • Settings toggles are now vertically centered with their labels. Toggles without a description (e.g. the Admin Columns and Admin Features rows) were top-aligned, so single-line labels sat above the switch's center. They now center cleanly; toggles that have a description keep their top-aligned layout.

1.0.12

June 22, 2026

Changed

  • The cache signature comment now includes an explicit Cached date — a human-readable site-local timestamp plus the Unix epoch — e.g. ... (generated in 38 ms) | Cached: 2026-06-22 04:59:40 PDT (1782129580) -->. The timestamp is frozen at cache-creation time, so it reflects exactly when each page was cached.

1.0.11

June 22, 2026

Fixed — Cache signature visible behind a CDN

  • The "Powered by Speed of Light" signature comment now appears on cache MISS responses too, not only on disk HITs. Previously the freshly-generated response (the copy a CDN like Cloudflare caches) was served without the comment, so behind an edge cache the credit/verification comment never showed. The signature is now stamped on the served response as well. Added a sol_cache_signature filter (return '' to white-label/remove it).

1.0.10

June 22, 2026

Improved — LCP / image discovery

  • The LCP optimizer now handles JS lazy-loaded hero images. Previously, when a theme or plugin lazy-loaded the hero via a data-src placeholder (lazysizes, WP Rocket, Blocksy, GreenShift, a3 Lazy Load, etc.), the optimizer skipped it entirely — so the LCP request stayed undiscoverable and lazy-loaded. It now detects data-src / data-srcset (and variants), preloads the real image (<link rel=preload as=image> with imagesrcset/imagesizes + fetchpriority=high), promotes the real URL into src/srcset, removes the lazy data-* attributes, and sets loading=eager. This satisfies the Lighthouse "LCP request is discoverable", "not lazy-loaded", and "fetchpriority=high applied" audits automatically.
  • Fixed an attribute-matching bug where src/srcset rewrites could collide with data-src/data-srcset (word boundary matched after the hyphen); attribute matching is now hyphen-aware, and URL values containing regex characters (e.g. $ in a query string) are inserted safely.

1.0.9

June 18, 2026

Added — Multilingual support (WPML / Polylang)

  • Language-aware page cache. The cache key now includes the active language (detected from the pll_language / wp-wpml_current_language / _icl_current_language cookies), so the same URL serves the correct translation. It is derived identically on the pre-WordPress serve path and the write path, and is a no-op on non-multilingual sites (the cache key is unchanged when no language cookie is present), so existing caches keep working.
  • Clear all translations on update. Editing, publishing, or deleting a post now also clears and re-warms its linked translations (WPML and Polylang), so a change in one language refreshes every language version instead of leaving translated pages stale. Filterable via sol_translation_urls.

1.0.8

June 18, 2026

Fixed — Edge Cache UI

  • The Edge Cache screen now correctly shows a configured Cloudflare connection. Because the API key is intentionally never sent back to the browser (security), the UI was misreading a saved connection as "not configured" and dropping back to the empty Account step. It now detects the saved connection via presence flags, opens directly to the Deploy step, and shows a "Cloudflare credentials saved" card with a Replace credentials option. "Re-list Cloudflare sites" now reuses the stored key server-side, so you never need to paste it again.

1.0.7

June 18, 2026

Packaging

  • Added a root readme.txt so plugin description, version, and changelog details render in the WordPress Plugins and Updates "View details" modal (used by Freemius).

1.0.6

June 18, 2026

Fixed — Critical: hover-preload could trash/delete posts in the admin

  • Hover-preload no longer runs in wp-admin. The "preload links on hover" feature was loading on admin screens and prefetching whatever link you hovered with a real authenticated request. In the Posts list this meant hovering a row's Trash/Delete action (a GET URL with a nonce) would execute it server-side — so a post got trashed/deleted on hover and the actual click then reported "Error in moving the item to Trash." / "This item has already been deleted." The script is now front-end only.
  • Hardened the prefetcher to never request action URLs even on the front end — it now skips any link containing _wpnonce, action=, add-to-cart, /wp-admin/, wp-login.php, /wp-json/, or wc-ajax (prevents accidental logout / add-to-cart / other side-effects on hover).

1.0.5

June 18, 2026

Fixed — Cache Freshness

  • Editing content now refreshes the page itself, not just its archives. In Warm Cache mode (the default), saving a post/page clears and automatically re-warms that entry's own URL — previously only its archives (home, category, tag) were refreshed, so an edited page could keep serving its old cached HTML until a manual clear. Deleted/unpublished URLs are cleared but not re-queued.

Fixed — Stability & Performance

  • Remove Unused CSS no longer risks a request timeout. The 3-second generation budget is now actually enforced inside the parse loop — on a CSS-heavy page the engine abandons generation and serves the original stylesheet instead of blocking the visitor long enough to trip max_execution_time.
  • Real User Monitoring no longer grows without bound. A daily WP-Cron job now prunes metrics older than the retention window (default 30 days); the ingestion endpoint caps the number of metrics per beacon and only accepts the five known Core Web Vitals.
  • Database overview / admin dashboards avoid repeated uncached full-table scans where possible.

Fixed — Image Optimization

  • Server-side and WP-CLI conversions now report savings correctly. Compression savings are stored as a ratio (and conversion time in milliseconds) to match the upload path, so the Media Library no longer shows negative or nonsensical "Saved %" values.

Security & Privacy

  • Cloudflare credentials hardening. The API token / Global API Key are no longer returned to the browser or written into settings exports (a masked indicator is shown instead), and the credential option is no longer autoloaded on every front-end request.
  • TLS verification enabled on Google Font and Gravatar downloads (previously skipped), and third-party self-hosting now matches on an exact provider host allowlist instead of a substring.
  • RUM privacy: stored page URLs are de-queried (query strings stripped) and a privacy-policy snippet is registered.
  • SVG uploads now default to OFF (still gated to administrators when enabled).
  • REST database-cleanup honours the same multisite restriction as the admin screen; notice-dismissal checks capability before nonce.

Fixed — Optimization correctness

  • The explicit data-sol-lcp background-image marker is now detected (the previous regex could never match).
  • ES module scripts written as type = "module" (with spaces) are correctly excluded from JS minification.
  • Hardened hot-path settings reads to avoid PHP 8 "undefined array key" warnings leaking into cached output.

1.0.3

June 17, 2026

Fixed — Data Integrity

  • Image conversion no longer corrupts serialized data. When rewriting old image URLs to WebP/AVIF, postmeta, options, and termmeta are now processed with serialization-aware replacement instead of a raw SQL REPLACE. The previous behavior could corrupt PHP-serialized values (page-builder layouts, _wp_attachment_metadata, theme options) because the .jpg.webp length change broke the serialized length headers. post_content (plain HTML) still uses a fast direct replace.

Changed — Performance & Reliability

  • Self-hosted third-party scripts now refresh in the background. Analytics/GTM/Pixel copies are refreshed via WP-Cron (stale-while-revalidate) instead of a blocking 15-second download on a visitor's request, removing a potential Time-To-First-Byte stall on uncached page generation. TLS verification is now enabled on these fetches.
  • Critical CSS minifier now protects quoted string literals before stripping comments, so content:"…" values and url("…") are never corrupted.

Fixed — JS Delay Safety

  • ES module scripts are no longer delayed (they are deferred by spec; re-injecting them breaks import ordering).
  • Inline scripts containing a literal </script> or document.write() are never delayed, preventing truncated/broken script reconstruction.

Fixed — Markup

  • fetchpriority="high" injection no longer produces malformed markup on self-closing <img /> tags.

1.0.2

June 17, 2026

Fixed

  • CSS minify & combine: corrected the string-preservation regexes that caused minification/combination to silently no-op (and emit PHP warnings). CSS is now actually minified and combined, with quoted-string and comment content preserved correctly.
  • Remove Unused CSS (RUCSS): fixed the ID-selector matcher (#id) that always errored, which could strip styles for ID-targeted selectors. ID selectors are now matched against the rendered DOM correctly.
  • CSS URL rewriting: guarded an undefined-array-key warning when rewriting url() references in root-level stylesheets.

Changed — Optimization & Ads

  • JS Delay now protects advertising and analytics by default: AdSense (adsbygoogle, pagead2.googlesyndication.com, GPT/googletag, DoubleClick), Google Funding Choices, Google Tag Manager / GA4 (gtag, gtm.js), and Facebook Pixel scripts are never delayed in any mode, so ads and measurement always render — even for visitors who don't interact. Also added Cloudflare Turnstile to the never-delay CAPTCHA list.
  • Cloudflare Edge rules: added dynamic-request bypasses to the deployed Cache Rule (wc-ajax=, add-to-cart=, remove_item=, unapproved=, preview=true, preview_id=) so cart/checkout AJAX and post previews are never edge-cached. Takes effect on next "Deploy Rules".

Documentation

  • Clarified that the Cloudflare integration uses the modern Cache Rules API (Free-plan compatible, consumes no Page Rules).
  • Corrected the installation folder path.

1.0.1

June 17, 2026

Changed

  • Declared compatibility with WordPress 7.0 (Tested up to: 7.0).

Fixed

  • Post/Page list rows: the "Featured Image" admin column no longer steals the list table's primary column, which had moved the Edit/Quick Edit/Trash/View row actions off the title (onto the thumbnail) and could cause accidental trashing. The title now keeps its row actions.

1.0.0

May 23, 2026

Added — Core Engine

  • Edge-aware HTML page cache via advanced-cache.php drop-in — serves cached pages before WordPress boots
  • Smart cache safety engine — auto-detects logged-in users, WooCommerce carts, commenters, and 30+ known cookies (WordPress core, WooCommerce, EDD, Jetpack, Wordfence, Cloudflare, and more)
  • Host validation — prevents cache poisoning on misconfigured multisite or reverse proxy setups
  • Gzip/Brotli-ready cache storage — stores compressed cache files when server supports it
  • Per-page cache exclusions — by URL path, query string, cookie regex, post type, or post ID
  • Atomic file writes for all cache operations — prevents cache corruption under load

Added — Preloader

  • Intelligent cache preloader — crawls and warms cache automatically after purge events
  • Turbo mode — high-concurrency preload for dedicated servers
  • Adaptive batch sizing — learns server throughput and auto-adjusts concurrency
  • Detailed diagnostic logging — HTTP codes, cache status, failure reasons, retry counts
  • Origin bypass — optionally bypass CDN and hit origin directly during preload
  • Stuck preload detection — automatic lock release with grace period
  • WP-CLI preload supportwp sol preload --turbo --run --origin

Added — Asset Optimization Pipeline

  • JS Minify — minifies local JS files and inline scripts using MatthiasMullie/Minify
  • CSS Optimize — minifies + combines stylesheets; inlines small CSS bundles (< 30KB)
  • Critical CSS — extracts above-fold CSS rules, inlines them, defers remaining stylesheets with FOUC protection
  • Font Optimizer — self-hosts Google Fonts, downloads WOFF2 files locally, injects preload hints
  • LCP Optimizer — detects the actual hero image (not just first <img>) via heuristic scoring; adds fetchpriority="high" + loading="eager" + preload hint
  • Third-Party Optimizer — self-hosts Google Analytics, GTM, and Facebook Pixel scripts locally with daily refresh
  • Embed Facades — replaces YouTube/Vimeo iframes with lightweight click-to-load placeholders; dramatically improves LCP and INP
  • Image Frontend — lazy-loads images and iframes, adds decoding="async", injects fetchpriority="high" for first image, reserves 16:9 aspect ratio for video embeds
  • JS Delay — WP Meteor-style interaction-based script loading with safe and aggressive modes; built-in critical script detection for jQuery, WooCommerce, payment gateways, CAPTCHA, and more

Added — Remove Unused CSS (RUCSS)

  • Server-side CSS tree-shaking using Sabberworm CSS Parser
  • Matches selectors against rendered HTML DOM
  • Generates a single used-CSS file per URL
  • Auto-safelist for WordPress core, WooCommerce, Elementor, GreenShift, Divi, Avada, Bricks, Beaver Builder, Breakdance, Oxygen, and 20+ slider/carousel libraries
  • Per-post disable via meta box
  • Database-tracked with status reporting (completed/failed/pending)
  • WP-CLI support: wp sol rucss clear and wp sol rucss stats

Added — Real User Monitoring (RUM)

  • Collects LCP, INP, CLS, FCP, TTFB from actual visitors via PerformanceObserver
  • Stores metrics in dedicated database table with automatic schema creation
  • Grade distribution reporting (Good / Needs Improvement / Poor) per metric
  • Worst-performing pages identification
  • LCP element tracking for debugging layout shift issues
  • Rate-limited ingestion (max 20 entries per IP per minute)

Added — Image Optimization (PixSOL)

  • Bulk WebP/AVIF conversion with configurable quality
  • SVG and AVIF upload support
  • Frontend lazy loading with LCP-aware eager loading
  • fetchpriority="high" injection for first image
  • CDN URL rewriting support
  • Per-image optimization status tracking
  • Bulk compression with pause/resume

Added — Database Health

  • Read-only database overview with 8 KPI cards (total size, posts, postmeta, transients, revisions, autoload size, engine, fragmentation)
  • Size trend chart (14-day history)
  • Storage engine distribution (InnoDB vs MyISAM)
  • Table size breakdown with top 8 largest tables
  • Safe cleanup — expired transients, auto-drafts, trash, spam comments, old revisions (with keep limit), orphaned postmeta/commentmeta
  • Autoloaded options insight with size warnings and source identification
  • Multisite-disabled by design for cross-site safety

Added — Kill Bloat

  • Disable emojis, embeds, XML-RPC, RSS feeds, REST API (selective)
  • Remove generator tags, RSD, WLW, shortlinks, pingbacks, X-Pingback headers
  • Disable jQuery Migrate, dashicons, block CSS, global styles
  • WooCommerce-specific optimizations: disable cart fragments on non-shop pages, block styles, non-shop assets
  • Heartbeat throttling, revision limiting, autosave interval control
  • Disable self-pingbacks, query string removal, Google Fonts disable

Added — Edge Cache / CDN Integration

  • Cloudflare integration — purge everything, deploy cache rules, update Page Rules via API
  • Custom cache headers with Cache-Control: public, max-age, s-maxage
  • X-Cache-Edge and X-Cache-Handler response headers for debugging
  • Cookie bypass configuration for logged-in state
  • Admin bar purge button with visual feedback
  • Automatic purge hooks: post save, comment, stock change, term edit, theme switch, menu update

Added — Compatibility Detection

  • Auto-detects 10+ themes (Blocksy, Kadence, GeneratePress, Astra, Neve, OceanWP, Flatsome, Avada, Divi)
  • Auto-detects 5+ page builders (GreenShift, Elementor, Oxygen, Bricks, Beaver Builder)
  • Auto-detects 15+ conflicting cache/optimization plugins (WP Rocket, LiteSpeed, W3 Total Cache, FlyingPress, NitroPack, Breeze, SG Optimizer, etc.)
  • Contextual warnings with severity levels (info / warning / critical)
  • Specific feature recommendations per detected plugin/theme

Added — Admin Interface

  • React-powered dashboard with real-time stats, charts, and controls
  • Three optimization modes: Developer, One-Click, Aggressive
  • Cache hit/miss ratio tracking
  • Preload progress with speed rate (URLs/second)
  • Settings import/export
  • Per-post meta box: disable RUCSS or JS minify on individual posts/pages
  • Admin columns: cache status, featured image, excerpt, last modified, ID
  • Dashboard widget: at-a-glance cache size
  • Admin bar items: clear cache, preload status, edge purge

Added — Developer Tools

  • WP-CLI commands: wp sol clear, wp sol preload, wp sol rucss clear, wp sol rucss stats, wp sol js-minify clear
  • REST API with 20+ endpoints for settings, stats, cache purge, preload, edge deploy, image compression, RUM data, DB cleanup
  • 30+ action and filter hooks for customization
  • Constants for module disabling and cache path customization
  • Logger class for structured debug logging

Added — Safety & Reliability

  • Never caches admin, login, REST API, XML-RPC, or POST requests
  • Never caches pages with WooCommerce/EDD session cookies
  • Respects DONOTCACHEPAGE constant
  • Automatic cache invalidation on content changes
  • Preload skip during cache generation to prevent timeout cascades
  • Critical CSS FOUC prevention — only defers stylesheets when substantial critical CSS exists
  • CSS combine skip for @import files
  • JS minify skip for module scripts and data URIs
  • RUCSS generation time limit (3 seconds) to prevent timeouts
  • Database table existence checks before all RUM queries

Added — Multisite Support

  • Network-aware cache directory structure
  • Per-blog cache isolation
  • Network admin notices
  • Multisite-safe database operations

Questions about a release?

Read the docs for how each feature works, or reach the team directly — we answer every message.