=== BaseCloud Boost ===
Contributors: basecloud
Tags: cache, performance, speed, optimization, caching
Requires at least: 5.5
Tested up to: 6.9
Requires PHP: 7.4
Stable tag: 1.3.8
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

World-class page caching, asset optimization, and performance acceleration for WordPress — by BaseCloud.

== Description ==

**BaseCloud Boost** is a professional-grade WordPress performance plugin that dramatically speeds up your website through intelligent full-page caching, asset optimization, and smart cache management.

= Core Features =

**Page Cache**

* Full-page HTML caching that bypasses WordPress and PHP entirely for maximum throughput
* GZIP and Brotli compression variants stored alongside each cached page
* Smart cache invalidation on post updates, comment submissions, and taxonomy changes
* Configurable cache lifetime (default: 7 days)
* Separate mobile device cache for responsive-aware caching
* Cache exclusion by URL pattern or cookie name

**Asset Optimization**

* HTML, CSS, and JavaScript minification
* CSS and JS file combining to reduce HTTP requests
* JavaScript deferral for faster first paint
* Critical CSS extraction and inline injection
* Remove query strings from static asset URLs for better CDN hit rates
* Async-load non-critical Google Fonts with font-display=swap

**Media Optimization**

* Reliable native lazy loading for images, iframes, and videos -- defers off-screen media without ever hiding it, so images always appear
* On-the-fly image compression -- generates high-quality WebP copies of your images on upload, plus a one-click Bulk Compress tool for your existing media library
* Quality-preserving compression -- originals are never altered; WebP copies are created alongside them at a tunable visually-lossless quality
* WebP and AVIF serving -- automatically serves the next-gen format when available
* Video facade for YouTube and Vimeo -- replaces iframes with click-to-play thumbnails
* preload="none" applied to video tags for faster page loads

**Cache Preloader**

* Automatic sitemap-based URL discovery
* Background batch processing to keep the cache warm
* Real-time progress tracking in the admin dashboard

**CDN Integration**

* Generic CDN hostname rewriting for any CDN provider
* Cloudflare API cache purging -- automatically clears Cloudflare edge cache on purge
* BunnyCDN API cache purging -- mirrors local purge events to your Pull Zone

**Database Optimization**

* Post revision cleanup
* Auto-draft and trashed post/comment removal
* Expired transient removal
* Orphaned postmeta cleanup
* Table optimization (OPTIMIZE TABLE)

**Security Headers**

* X-Content-Type-Options, X-Frame-Options, Referrer-Policy
* Permissions-Policy (FLoC/Topics API opt-out)
* Strict-Transport-Security (HSTS) for HTTPS sites

**Developer-Friendly**

* Full hook API: filter cache behaviour, modify HTML before write, extend CDN purge logic
* WP-CLI commands for cache management
* PSR-4 autoloaded class architecture

== Installation ==

1. Upload the `basecloud-boost` folder to the `/wp-content/plugins/` directory.
2. Activate the plugin through the 'Plugins' menu in WordPress.
3. Navigate to **BaseCloud Boost > Dashboard** to configure.
4. Enable Page Cache and click **Boost Cache** to start caching immediately.

== Frequently Asked Questions ==

= Does BaseCloud Boost work with WooCommerce? =

Yes. Cart, checkout, and My Account pages are automatically excluded from caching. Active WooCommerce cart session cookies bypass the cache transparently.

= Is it compatible with WordPress Multisite? =

Yes. BaseCloud Boost supports WordPress Multisite with per-site cache directories.

= Can I use it with Cloudflare or BunnyCDN? =

Yes. BaseCloud Boost includes Cloudflare and BunnyCDN API integrations. When you purge the local page cache, the plugin automatically purges the corresponding edge cache too.

= Will it conflict with other caching plugins? =

Running multiple full-page caching plugins simultaneously is not recommended and can produce unexpected results. BaseCloud Boost detects and warns you about other active caching plugins on the Dashboard.

= What if my site breaks after activating the plugin? =

Deactivate the plugin from the Plugins screen. This automatically removes the advanced-cache.php drop-in and disables WP_CACHE, restoring your site to its previous state.

= Does BaseCloud Boost modify wp-config.php? =

Yes. On activation it sets `define( 'WP_CACHE', true )` in `wp-config.php` so WordPress loads the `advanced-cache.php` drop-in. On deactivation this line is removed. The change is minimal and clearly labelled so it is easy to identify.

== External Services ==

BaseCloud Boost connects to the following external services **only when you explicitly configure them** in the plugin settings. No data is sent to any third-party service by default.

= Cloudflare Cache Purge API (Optional) =

If you enable Cloudflare CDN integration and provide a Zone ID and API Token, the plugin calls the Cloudflare API to purge cached content whenever your local cache is cleared.

* Service: Cloudflare, Inc.
* What it is used for: Purging edge-cached pages so visitors see fresh content after a cache clear.
* When data is sent: Only when you trigger a cache purge (manually, on post save, or via plugin action).
* Data sent: List of URLs to purge and your Cloudflare Zone ID (via your API token).
* API endpoint: https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache
* Terms of Service: https://www.cloudflare.com/terms/
* Privacy Policy: https://www.cloudflare.com/privacypolicy/

= BunnyCDN Cache Purge API (Optional) =

If you enable BunnyCDN integration and provide an API Key and Pull Zone ID, the plugin calls the BunnyCDN API to purge cached content whenever your local cache is cleared.

* Service: BunnyWay d.o.o. (BunnyCDN)
* What it is used for: Purging Pull Zone edge cache so visitors receive fresh content.
* When data is sent: Only when you trigger a cache purge.
* Data sent: URLs to purge and your API key.
* API endpoints: https://api.bunny.net/purge and https://api.bunny.net/pullzone/{id}/purgeCache
* Terms of Service: https://bunny.net/tos/
* Privacy Policy: https://bunny.net/privacy/

= Performance Metrics Webhook (Optional) =

If you configure a webhook URL in the plugin settings, BaseCloud Boost will POST a JSON payload containing anonymous performance metrics to that URL on a daily cron schedule.

* Service: Custom endpoint configured by you.
* What it is used for: Sending performance data to an external monitoring or reporting system.
* When data is sent: Once per day via a scheduled cron event, and when you manually send a test from the admin panel.
* Data sent: Cache hit rate, cache size, bytes saved (HTML/CSS/JS), last purge time, plugin version, site URL, and site name. No user data or passwords are included.
* Endpoint: Your custom URL -- you are responsible for its security and privacy compliance.

= Google PageSpeed Insights API (Optional) =

If you enter a PageSpeed Insights API key in the Lighthouse settings, the plugin calls Google's PageSpeed Insights API to run automated Lighthouse audits for your site.

* Service: Google LLC (PageSpeed Insights)
* What it is used for: Running automated Lighthouse performance audits (Performance, Accessibility, Best Practices, SEO scores).
* When data is sent: When you manually trigger an audit from the Lighthouse settings page, or on the scheduled Lighthouse cron (if enabled).
* Data sent: Your site URL and your API key.
* API endpoint: https://www.googleapis.com/pagespeedonline/v5/runPagespeed
* Terms of Service: https://developers.google.com/terms
* Privacy Policy: https://policies.google.com/privacy

= Vimeo oEmbed API (Conditional) =

If a page contains a Vimeo video facade, the plugin's frontend JavaScript fetches the video thumbnail from the Vimeo public oEmbed API. This happens in the visitor's browser, not on the server.

* Service: Vimeo, Inc.
* What it is used for: Retrieving the video thumbnail image to display in the click-to-play facade.
* When data is sent: When a page containing a Vimeo video facade is viewed by a visitor.
* Data sent: The Vimeo video ID (no user data or authentication is required).
* API endpoint: https://vimeo.com/api/v2/video/{id}.json
* Terms of Service: https://vimeo.com/terms
* Privacy Policy: https://vimeo.com/privacy

= Google Tag Manager / Google Fonts Preconnect Hints (Conditional) =

When the Resource Hints module is enabled, the plugin outputs `<link rel="preconnect">` and `<link rel="dns-prefetch">` hints for common third-party origins (Google Tag Manager, Google Analytics, Google Fonts, jsDelivr/cdnjs). These are passive hints that tell the browser to pre-establish connections — no data is sent by the plugin itself.

* Service: Various (Google LLC, Cloudflare, Inc.)
* What it is used for: Reducing connection latency for third-party resources already loaded by your theme or other plugins.
* When data is sent: The browser (not the plugin) initiates the connection. No plugin data is transmitted.
* Terms of Service / Privacy: Governed by the respective third-party services.

All remote requests originating from the plugin server-side use WordPress's built-in wp_remote_post() / wp_remote_get() functions and respect your server's SSL configuration.

== Screenshots ==

1. Dashboard -- live cache statistics, feature status overview, and one-click cache clear.
2. Page Cache settings -- configure cache lifetime, exclusions, and mobile cache separation.
3. File Optimization -- HTML, CSS, and JS minification and combining controls.
4. Media settings -- lazy loading, WebP/AVIF serving, and video facade options.
5. CDN settings -- Cloudflare and BunnyCDN API integration configuration.
6. Database Optimizer -- one-click cleanup for revisions, transients, and orphaned data.

== Changelog ==

= 1.3.8 =

**Hotfix: video and iframe embeds render reliably again**

* **Fixed YouTube/Vimeo embeds erroring in the editor and rendering empty on the site.** The "Disable Embeds" optimization (on by default) removed not just WordPress's embed *assets* but its embed *rendering* machinery — the editor's embed preview returned "Sorry, this content could not be embedded", and a URL-based embed could resolve to nothing and then be frozen empty by the page cache. The toggle now does only its real job: stop serving wp-embed.min.js and this site's embed-discovery links. Embed rendering can no longer be affected by it. If a page cached an empty embed, one Clear Cache repairs it.
* **Changed: the click-to-play video facade is now opt-in (was on by default).** Replacing YouTube/Vimeo players with thumbnail facades is an aggressive optimization: it changes playback to two clicks, conflicts with builder video widgets, and errors outright on sites whose security headers deny autoplay/encrypted-media. Sites that want it can enable "Video Facade" deliberately; its play-button icon (previously invisible due to a rendering bug) is also fixed.
* **Fixed autoplaying hero/background videos being demoted to `preload="none"`.** An autoplay video must start buffering immediately — its poster is usually the page's LCP. Only passive click-to-play videos are now demoted.

= 1.3.7 =

**Elementor Speed (opt-in), a redesigned Light/Dark admin, the Fleet Ops read-only API, and an HTML-freshness correctness fix**

* **New: Elementor Speed — Boost finally optimizes Elementor instead of only protecting it (opt-in).** Most Elementor sites carry the same invisible weight: 4–27 tiny render-blocking `post-*.css` requests per page, an always-loaded `eicons` icon stylesheet, and Font Awesome shims. The new module (File Optimization → Elementor Speed, master toggle **off** until you enable it) inlines each page's own Elementor CSS straight into the HTML (identical rules, same position, minus every one of those blocking requests), drops the eicons/Font Awesome stylesheets only on pages that provably use no icons, lightboxes, popups or carousels (any doubt = the sheet stays), and adds a read-only advisor that flags Elementor settings worth changing (font-display swap, Inline Font Icons, Element Caching alongside a page cache). Elementor's own JavaScript and init order are never touched — that is exactly what historically breaks sliders and forms — and the advisor also warns on oversized DOM (Boost will never restructure your markup; the fix belongs in the builder).
* **New: the Boost admin now has true Light and Dark modes.** Appearance → Interface Mode switches the whole panel — every card, dropdown, text field and toggle re-themes with full contrast in both modes, previewing instantly. Typography is refreshed with a premium display face (Space Grotesk) over Inter. The bright Goop WebGL backdrop is retired (existing installs migrate to Dark automatically); the Classic particles and all six ambient video backdrops remain as optional dark-mode extras. Your four accent colours now adapt automatically so text stays readable on light surfaces whatever colours you pick.
* **Preview: Fleet Ops (BaseCloud CRM) — visible on the Advanced screen, marked "Unavailable Right Now".** The plugin side of BaseCloud's central monitoring (a read-only `bcboost/v1` REST API with a revocable, encrypted pairing key and optional signed requests) ships in this release but stays fully dormant — the API does not register and there is nothing to configure — until the BaseCloud service goes live in a future update.
* **Fixed stale HTML on hosts with an edge cache when "Browser Cache Headers" was off.** That toggle previously gated *all* Cache-Control output — with it off, HTML went out with no Cache-Control at all, so browsers heuristic-cached pages that no server-side purge could ever reach (found live on an Oxygen/Varnish site). HTML freshness headers (`max-age=0, must-revalidate`, plus logged-in no-store protection) are now sent unconditionally — the toggle only gates the static-asset caching rules, as it always should have.
* **Improved: async-CSS protection patterns can now be precise.** Exclusion entries accept `regex:` patterns alongside substrings, so Oxygen's numeric per-page stylesheets are protected exactly — while its site-wide sheets stay eligible for minify/combine/async. An invalid pattern fails closed (the stylesheet stays render-blocking) and is logged: a typo can slow a page, never break one.
* **Improved: cross-origin video-background heroes get a preconnect.** Hero videos streamed from a different origin (Azure blob, S3, video CDN) now have their connection warmed before the first byte is requested, the same treatment image heroes already get.
* **Improved: GZIP compression rules are always written to .htaccess** — compression is a correctness-neutral win and no longer depends on the browser-cache toggle.

= 1.3.6 =

**New: a built-in Critical CSS generator (Beta, opt-in) — the key that unlocks async CSS**

* **Boost can now create critical CSS itself — no Node, no headless Chrome, no external service.** The number-one thing holding back mobile scores on most sites is a wall of render-blocking stylesheets, and the cure (inlining the above-the-fold "critical" CSS, then loading the rest asynchronously) previously required generating that CSS with external tooling. The new built-in generator does it in pure PHP on your own server: it reads each template's page, determines which stylesheet rules the above-the-fold content actually uses, and fills the Per-Template Critical CSS store at the press of a button.
* **You choose — nothing turns itself on.** The generator ships **off**; enable it under Advanced → Critical CSS ("Built-In Critical CSS Generator (Beta)"), press *Generate for all templates*, and review the results per template. Inlining (Enable Critical CSS) and Async CSS (File Optimization) remain separate switches you flip yourself, and the long-standing safety gate is unchanged: any page without critical-CSS coverage automatically keeps safe render-blocking CSS — a page can never paint unstyled because coverage was missing.
* **New: six ambient video backdrops for the Boost admin.** The Appearance screen now offers looping video themes — Boost Rocket, Goop Motion, Lava Lamp, Colours, Fish Tank, and Zombies — behind the glass UI, alongside the Goop, Classic, and Minimal engines. Hover a card to preview its motion before choosing. Heavily compressed (all six together add ~4 MB to the plugin, sources were 140 MB), admin-only (never loaded on your website), paused in hidden tabs, and reduced-motion users get a still frame.
* **Improved: smarter preconnects.** Boost no longer preconnects to a fixed list of third-party origins on every page — it now checks which origins the page actually references (Tag Manager, Analytics, Google Fonts…) and hints only those, capped at the browser's useful budget of four. Unused preconnects were burning TLS handshakes inside the critical first-paint window and drawing Lighthouse's "preconnect not used" warning.
* **Improved: one hero per page — competing image priorities are demoted.** When the detected hero is a background, slider, or video-poster image, WordPress core (or the theme) may still have marked a different content image `fetchpriority="high"` — two "highest priority" images then fight for the same critical bandwidth and both paint later. Boost now removes the competing marker (returning those images to normal browser priority, never forcing them low) so the real hero wins the connection.
* **Improved: the cache preloader now warms the mobile variant too.** With "Separate mobile cache" enabled, the preloader previously warmed only the desktop copy of each page — phone visitors (and Google's mobile speed test) always hit a cold cache first. Every warmed URL is now fetched with a desktop and a mobile browser identity, so both variants are hot.
* **Built defensively.** Same-origin stylesheets are read from disk only (never fetched over the network), with strict file-count and size caps; generated output is size-capped, sanitized like every other critical-CSS source, and ambiguous selectors resolve toward keeping a rule (worst case a few extra bytes — never missing styles). A configured remote generation-service endpoint still takes precedence when you have one, and existing manual/CLI workflows are untouched. Regenerate any time from the same button — e.g. after the "may be stale" banner appears following a theme or builder change.

= 1.3.5 =

**Mobile speed release: the hero image starts downloading sooner, above-the-fold images are never lazy-loaded, lazy images download at their true rendered size, and icon fonts no longer flash placeholder glyphs**

* **New: automatic preconnect to the hero image's origin and your CDN.** The browser now opens the network connection (DNS + TCP + TLS) to the CDN and to whichever origin serves the page's hero image *before* it is asked to download it, saving a full round-trip on high-latency mobile connections — the hero starts downloading sooner, directly improving Largest Contentful Paint (LCP). A safe, standards-based resource hint (https, cross-origin only, never duplicated); toggle under Media → "Auto-Preconnect Hero & CDN Origin".
* **New: leading above-the-fold images are kept eager.** On phones the first screen usually holds 2–3 images, and lazy-loading them delays first paint (PageSpeed's "LCP image was lazy-loaded" flag). The hero plus the next above-the-fold image now always load immediately; the count is tunable under Media → "Above-the-Fold Images Kept Eager". Below-the-fold images are additionally hinted `fetchpriority="low"` so the hero keeps the bandwidth.
* **New: lazy images download at their true rendered size.** Every lazy-loaded responsive image now carries `sizes="auto"` (the WordPress 6.7 core mechanism, applied by Boost on older WordPress and page-builder markup too): supporting browsers pick the image variant that matches how large the image actually renders instead of a viewport-width guess — on phones this substantially cuts image bytes and fixes the PageSpeed "Properly size images" flag. Older browsers simply ignore it. Toggle under Media → "Right-Size Lazy Images".
* **Fixed icon fonts flashing a wrong character or placeholder box while loading.** The font-display optimisation applied `swap` to *every* font, including icon fonts (Font Awesome, Dashicons, Divi/Elementor icon packs, WooCommerce stars) — during the swap window the fallback font would paint a meaningless letter or tofu box where each icon belongs, most visibly on slow mobile connections. Icon fonts are now detected (by name and by their private-use-area unicode-range) and get `block` — briefly invisible, then the correct icon — while text fonts keep `swap` so real copy is never invisible. Detection matches font family names and file names only, never directory paths, so a text font that merely lives in a theme or plugin folder with an icon-like name is untouched.
* **Improved: more third-party scripts can be safely delayed.** The curated delay list (analytics, heatmaps, chat widgets, ad pixels that never render page content) now also covers Zopim/Zendesk Chat, Tidio, Smartsupp, Chatra, Olark, Zoho SalesIQ, Freshchat, Userlike, Clicky, Crazy Egg, Lucky Orange, Inspectlet, Smartlook, Heap, Fathom, Simple Analytics, Matomo Cloud, Criteo, Outbrain, Reddit Ads, and Quora Ads. Vendors whose scripts can render visible page content (Freshworks CDN, affiliate attribution tags) were deliberately reviewed and excluded.
* **Improved: hero detection now reads Elementor's external page CSS.** When Elementor stores a page's styles as an external file (its default mode), a section's background hero lives in `uploads/elementor/css/post-*.css` where detection previously could not see it — those pages silently got no hero preload. The detector now reads just those local files (never a network fetch, tightly capped) so external-file Elementor heroes are preloaded like everyone else's. Sections with different desktop/mobile backgrounds are still deliberately skipped rather than guessed.
* **Changed: image serving is now WebP-only by default — AVIF becomes a deliberate opt-in.** AVIF produced broken or blank images on servers whose encoder emits files it cannot verify (even with 1.3.4's validation, hosts without an AVIF decoder can't prove their output), while WebP delivers nearly the same savings with rock-solid reliability. Existing installs are switched to WebP-only once on upgrade (a dismissible notice explains this); your original images and any existing .avif files are untouched, and re-enabling AVIF under Media settings afterwards is respected forever.
* **New: WebM video served automatically when a copy exists.** Boost never converts video on your server (PHP can't, and re-encoding would be slow and lossy) — but when you place a .webm copy next to a self-hosted .mp4, Boost now injects it as the preferred source so supporting browsers stream the much smaller WebM while everyone else keeps the MP4. Same trusted sibling pattern as WebP images; CDN rewriting applies to the WebM URL like any other asset.
* **New: hero background videos get their poster preloaded.** On pages whose hero is a self-hosted video, the video frames can never paint first — the poster image is what visitors actually see, making it the true Largest Contentful Paint element. Hero detection now recognises an above-the-fold video's poster and preloads it with high priority. (No poster set? Add one — it is the single biggest LCP fix for video headers; a tip in Media settings explains this.)
* **New: Appearance settings — make Boost yours.** A new Appearance screen lets you pick the admin backdrop — **Goop** (the new lava-lamp: rocket-fuel plasma rising through deep space, a self-contained 6 KB WebGL shader replacing the previous 600 KB animation library), **Classic** (the original drifting particles), or **Minimal** (static gradient) — and re-tint the entire admin from four accent colour pickers, with instant live preview as you choose. Purely cosmetic: nothing on this screen affects your website or its speed. The goop backdrop pauses in hidden tabs, respects reduced-motion preferences, survives GPU resets, and falls back to the gradient wherever WebGL is unavailable.

= 1.3.4 =

**Corrupt WebP/AVIF conversions can no longer be served: conversions are now written atomically and fully verified before use, and a new repair command fixes existing broken files**

* **Fixed a corrupted WebP/AVIF conversion being served as a broken or blank image.** A conversion whose write was interrupted (e.g. a killed server process) or that a faulty server encoder produced could pass Boost's checks — its file header and pixel dimensions looked valid — and be served in place of the original, rendering as a broken/blank image in the browser. Conversions are now written atomically (a partially-written file can never land at the servable location) and must fully decode before they are ever used — verified with an independent image engine where the server has one, so the encoder is never the only judge of its own output. At serve time, files are additionally checked for structural truncation (declared size vs. actual bytes); anything that can't be validated is never served — the original image is always used instead, and a `WP_DEBUG_LOG` entry names the offending file once.
* **AVIF conversion is now skipped on servers that can't verify their own AVIF output.** If the server has no AVIF decoder to validate with, Boost no longer produces AVIF files it cannot check (WebP is unaffected); a `WP_DEBUG_LOG` entry explains this once.
* **New: `wp bcboost verify-conversions` repairs existing conversions.** The WP-CLI command scans every existing WebP/AVIF conversion, reports corrupt ones, and with `--delete-broken --regenerate` removes and rebuilds them (batchable via `--batch`). It also cleans up any orphaned temporary files. Safe everywhere: separately-uploaded files that merely share a name with an original (the 1.3.2 protection) and animated WebP files are never touched — but Boost's *own* conversions that turn out broken are now regenerated instead of being skipped forever.

= 1.3.3 =

**Hero-image detection targets the right image: 4-digit-width images lazy-load again, theme background bands are no longer preloaded, and detection now reads inline styles**

* **Fixed images with 4-digit widths (e.g. `width="1000"`) being wrongly treated as tracking pixels.** The tracking-pixel check matched any width/height *starting* with `1`, so full-size content images with widths like 1000 or 1200 were silently excluded from lazy-loading *and* from hero (LCP) prioritisation. These images now lazy-load normally and are eligible to be the preloaded hero again; only genuine 1×1 pixels remain exempt.
* **Fixed the hero preload targeting the wrong image on some themes.** Detection could latch onto a theme background band declared via a CSS custom property (e.g. Avada's `--awb-background-image`) or onto a small thumbnail instead of the real hero. Custom-property declarations are no longer mistaken for inline backgrounds, thumbnails and other insignificant images (logos, icons, `thumb`-named files) are vetoed, and affected pages self-correct on the next cache clear.
* **Improved: hero detection now also reads the page's own inline `<style>` blocks** (Elementor-embedded CSS and Critical CSS) to find section background heroes — it never fetches external stylesheets to do so. It also skips images hidden on mobile (e.g. `elementor-hidden-mobile` wrappers), and when it cannot confidently identify the hero it now emits nothing rather than guessing — a per-page `bcboost_lcp_image` custom field remains available as the manual override (a `WP_DEBUG_LOG` hint names it when detection stands down).

= 1.3.2 =

**Hotfix: WebP/AVIF serving no longer picks up unrelated same-name files, and hidden images are no longer treated as the hero**

* **Fixed WebP/AVIF serving displaying the wrong image when a separately-uploaded file shared a filename with an image Boost optimized.** If your Media Library contained both `photo.jpg` and an unrelated `photo.webp` that you uploaded yourself, Boost's modern-format serving assumed the `.webp` was its own conversion of the `.jpg` and served it in its place — showing a completely different (or distorted) image on the page. Boost now verifies that a `.webp`/`.avif` sibling really is a conversion of the original — its pixel dimensions must match the original exactly — before ever serving it; anything that doesn't match is left alone and the original image is served (behaviour tunable via the `BCBOOST_verify_modern_conversion` filter). Bulk image compression likewise now recognises such separately-uploaded files and will never overwrite or count them as its own conversions. No action needed: affected pages self-correct on the next cache clear, and none of your files were modified.
* **Hero-image detection now ignores images inside hidden containers and inactive tab panels.** Images inside `aria-hidden`/`hidden`/`display:none` containers or non-active tab panels are no longer promoted or preloaded as the page's LCP hero, so the *visible* hero gets the priority treatment.

= 1.3.1 =

**Hotfix: settings saves no longer appear to "not stick" on Redis / persistent-object-cache hosts**

* **Fixed settings toggles appearing not to save on hosts with a persistent object cache (e.g. Redis on Cloudways).** Saving a setting while the 1.3.0 cache preloader was running could look like the save was ignored: the value *was* stored correctly in the database, but a WordPress object-cache race (trac #31245) let a concurrent request write an older copy of the autoloaded-options blob back over it, so the settings page — and the plugin — kept reading the old value. Boost's settings now live outside the shared autoloaded options blob (so the race can never touch them again), every settings write goes through a single hardened path that refreshes the object cache immediately, and the settings page always redisplays values fresh from the database after a save. **No settings were ever lost**, and no action is needed: the fix repairs itself on the first wp-admin page load after updating — any toggles that looked "lost" reappear with their saved values.

**Fresh content guaranteed after every cache clear, full Divi support, a real LCP optimizer, per-template Critical CSS, and a cache preloader that re-warms your whole site**

* **Fixed "my visitors still see the old site even after I cleared the cache".** Boost told browsers to keep every CSS/JS file for a year without re-checking (`immutable`), while the *Remove Query Strings* option stripped the version token that would have busted that cache — so an edited stylesheet kept the same URL and browsers never re-fetched it. Now only Boost's own content-fingerprinted minified files (whose URL changes whenever their content changes) are marked immutable; every other CSS/JS file is always revalidated by the browser (a cheap 304 when unchanged). *Remove Query Strings* is now **off by default** — existing sites are migrated once automatically, with an admin notice explaining why — and re-enabling it now substitutes a safe file-timestamp version instead of removing the token entirely. Your edits show up for every visitor immediately after a cache clear, no hard refresh needed.
* **Boost Cache now clears everything and tells you exactly what it cleared.** Clearing the cache now also wipes Boost's minified/combined CSS and JS copies (previously they could survive a purge and keep serving stale code), and the toast shows an itemised summary of what was actually purged — pages, minified assets, CDN, Varnish — instead of a blanket "cache cleared". Clearing the cache also no longer deletes the plugin's settings snapshot (`bcboost-options.json`); it is preserved and regenerated automatically.
* **New: full Divi compatibility.** All optimizations now stand down inside the Divi Visual Builder, backend builder and previews, so editing in Divi always works. Boost automatically purges the right pages when you save in the Divi builder, change Theme Options, or Divi regenerates its static CSS — and warns you when Divi's own minify/combine settings overlap Boost's so the two never double-process your files. Divi's `/et-cache/` files are also excluded from async CSS and query-string handling.
* **All optimizations now stand down inside every major page-builder editor.** Elementor, Divi, Beaver Builder, WPBakery, Bricks, Oxygen, OptimizePress, Thrive and the WordPress Customizer are all detected — front-end editors, previews and builder AJAX requests are served completely untouched, so Boost can never break an editing session.
* **New: LCP Optimizer — your hero image loads first.** Boost now detects the page's real hero — a regular image, a CSS background, a static slider's first slide, or a background hidden inside the combined-CSS bundle — and preloads it with `fetchpriority="high"`, preferring the WebP/AVIF copy when one exists. Works with Combine CSS on or off, and a per-page override lets you pin the hero when detection can't see it. This targets the single biggest mobile PageSpeed lever: Largest Contentful Paint.
* **New: per-template Critical CSS.** Store above-the-fold CSS per template (front page, posts, pages, products, archives) with per-URL overrides, a management UI under Advanced, WP-CLI commands (`wp bcboost critical-css`), and optional remote generation (off by default). Boost tracks when stored critical CSS may have gone stale (theme switch, builder CSS regeneration) and flags it. Async CSS now activates **per page**, only where critical CSS actually exists — and per-page builder CSS always stays render-blocking — so async delivery can no longer unstyle a page.
* **New: chained cache preloader — your site re-warms itself in minutes.** After a cache clear, Boost now crawls and re-caches the whole site via secured self-requests, with no dependence on visitor traffic or unreliable WP-Cron intervals: honest live progress in the dashboard, a watchdog plus cron fallback if a link in the chain dies, automatic stand-down on managed hosts that block loopbacks, and re-warming of a post's pages right after you update it. "Preload After Cache Clear" now actually works.
* **JavaScript minifier rewritten with full template-literal support.** Modern scripts using nested template literals, expressions inside `${…}`, tagged templates and regex literals are now minified correctly (54-test regression suite, verified against Node for semantic equivalence), and a fail-open guard serves any script it cannot safely parse unminified instead of ever breaking it. Minification of inline `<script>` blocks is now a separate, off-by-default advanced toggle, since inline code written by themes/plugins is the riskiest to touch.
* **HTML minifier is safer with comments.** IE conditional comments (both hidden and revealed forms) and allowlisted comments (e.g. `noptimize` markers) are now preserved instead of stripped, and the minifier runs first in the pipeline so it can no longer interfere with other optimizations.
* **Bulk image compression rebuilt for reliability.** The Bulk Compress tool now falls back from GD to Imagick automatically, respects time and memory budgets with adaptive batch sizes, reports per-image failures in the UI instead of stalling silently, resumes safely after a crash (a poison-pill image can no longer wedge the queue), produces AVIF where the server supports it, and gains a `wp bcboost compress` WP-CLI command. When the server can't produce WebP/AVIF at all, the Media screen now says so plainly instead of failing quietly.
* **Fixed a PHP 7.4 fatal error in resource hints** that occurred when font preloads were configured. Self-hosted Google Fonts now preload their first two woff2 files for a faster first paint. The page-cache default lifetime is now 7 days instead of 24 hours (existing sites keep their configured value).

= 1.2.3 =

**Safe-by-default JS optimization, automatic Cloudways purging, and Tools in the menu**

* **New: Safe Mode for Defer/Delay JS (on by default).** Instead of delaying *all* JavaScript and excluding what breaks, Boost now delays **only** a curated allowlist of known third-party scripts — analytics, tag managers, ad/conversion pixels and external chat widgets — and **never touches your own JavaScript** (sliders, testimonials, forms, Elementor/Divi, your theme). This keeps most of the Total-Blocking-Time win while making Defer/Delay JS safe by design, so it can no longer break interactive elements. Power users can switch Safe Mode off for the previous "delay everything except the exclusion lists" behaviour. Allowlist is filterable via `BCBOOST_third_party_allowlist`.
* **New: automatic Cloudways Varnish purging.** On Cloudways, Varnish sits in front of the site and would keep serving old pages after a cache clear. Boost now detects Cloudways and **purges its Varnish automatically whenever you clear the cache** — no configuration, and it survives plugin reinstalls — so your edits show immediately. Any Varnish settings you've configured yourself are still respected; non-Cloudways sites are unaffected. Opt out with `BCBOOST_auto_cloudways_varnish`.
* **Tools is now a page inside the BaseCloud Boost menu** (export/import settings, etc.), instead of only appearing in the WordPress sidebar submenu.

= 1.2.2 =

**Managed-host safety, builder-safe JS optimization, and on-demand WebP**

* **Never breaks managed hosts (WP Engine, Kinsta, Pagely, Pantheon, VIP).** Those hosts run their own page cache and forbid third-party page-cache drop-ins. Boost no longer installs its `advanced-cache.php` drop-in on them (and removes its own if an earlier version left one), so it can run safely for asset optimisation while the host handles caching. As an extra safety net, the drop-in is now wrapped so any unexpected error makes it fall back to normal WordPress instead of being able to take a site offline.
* **Defer/Delay JS no longer breaks forms or sliders.** The built-in exclusion list now also keeps form runtimes (Gravity Forms, Contact Form 7, WPForms, Fluent Forms, Ninja Forms, Forminator) and hero sliders (Slider Revolution, LayerSlider, Smart Slider 3, MasterSlider, MetaSlider) loading normally. Previously, delaying their JS could leave a form collapsed/unstyled until interaction, or a slider hero appearing only after a tap (a severe LCP regression). Third-party scripts (analytics, tag managers, pixels, chat) are still deferred/delayed. All overridable via the `BCBOOST_default_js_excludes` filter.
* **New: convert specific images to WebP from the Media Library.** A "Convert to WebP" row action and a "Convert to WebP (BaseCloud Boost)" bulk action let you optimise individual or selected images on demand — e.g. just your hero/LCP image — without running the full library conversion. The existing one-click Bulk Compress tool remains for site-wide conversion.
* **Better LCP: the header logo is no longer mistaken for the hero.** Themes often mark the logo `fetchpriority="high"`; Boost now ignores a high-priority logo when choosing which image to preload, so the real above-the-fold hero is preloaded instead.
* **Less layout shift (CLS).** Images that are missing both `width` and `height` now get their intrinsic dimensions added automatically when derivable from the WordPress filename, so the browser reserves space and the page doesn't jump as images load.
* **BunnyCDN Optimizer awareness.** A new "Bunny Optimizer is enabled on this Pull Zone" option (CDN settings). When enabled, Boost stands down its own image-format conversion — it won't rewrite images to `.webp` or generate WebP copies — and lets BunnyCDN's Optimizer negotiate WebP/AVIF (with fallback) at the edge for every visitor, so the two never overlap. CDN URL rewriting still applies.

= 1.2.1 =

**Defer/Delay JS: keep the whole Elementor chain together so headers and menus work**

* **Fixed Elementor navigation/headers breaking under Defer or Delay JS.** The 1.2.0 default exclusions kept Elementor's script files but not the inline config they depend on (`elementorFrontendConfig`, `ElementorProFrontendConfig`), the `js-cookie` (`Cookies`) dependency, or the bundled libraries under `assets/lib/` (SmartMenus, sticky header, Swiper). With those delayed, the Elementor scripts ran before their config/libraries existed and threw "not defined" errors, so menus, sticky headers and sliders never initialised. The built-in exclusion list now covers the entire Elementor + Elementor Pro footprint (scripts, inline configs, and `lib/` helpers) plus `js-cookie`, so they load together and initialise correctly — while analytics, tag managers, chat, forms and WooCommerce scripts are still deferred/delayed. Still overridable via `BCBOOST_default_js_excludes`.

= 1.2.0 =

**More robust CSS minification + safer Async CSS and JS optimization on page-builder sites**

* **Fixed a CSS minification bug that could delete large blocks of CSS and break styling (e.g. Gravity Forms).** When a CSS comment contained an apostrophe or quote — for example `/* inherit Elementor's font */` — the minifier mistook that quote for the start of a CSS string and swallowed everything up to the next quote, often hundreds of lines away. The affected CSS (including custom form styling) was then dropped, making elements fall back to their default appearance. Comments and strings are now parsed together in a single pass, so a quote inside a comment can never start a string, and a comment sequence inside a string is never stripped. This also replaces an older comment-stripping pattern that could be slow on very large stylesheets.
* **Async CSS now activates only when Critical CSS is present.** Loading CSS asynchronously without inlined Critical CSS defers every stylesheet — including each page-builder template's per-element rules — past the first paint, so builder pages (Elementor, etc.) could render with default styling until the stylesheet arrived: unstyled headings, missing section background overlays, mis-sized widgets. Async CSS is now a safe no-op until Critical CSS is filled in, and the setting explains this. CSS stays render-blocking (correct layout) in the meantime.
* **JavaScript Defer/Delay no longer breaks page-builder menus out of the box.** Boost now ships a built-in exclusion list for the jQuery + Elementor + Elementor Pro + SmartMenus initialization chain, so navigation menus and dropdowns bind correctly while everything else (analytics, tag managers, chat, forms) is still deferred/delayed for a lower main-thread cost. The list is filterable via `BCBOOST_default_js_excludes`.

= 1.1.9 =

**Dashboard: honest cache status when a reverse proxy is in front**

* **The dashboard now explains the hit rate when a reverse proxy (Varnish) is active.** On stacks like Cloudways, a reverse proxy caches Boost's optimized pages and serves most visits without ever reaching PHP — so Boost's hit counter (which only sees PHP requests) reads 0% even though every page is cached and served fast. When Varnish purging is enabled (or a proxy is detected), the dashboard now shows a short note clarifying that pages are cached at the proxy and the hit rate reflects only direct PHP requests, so the 0% is no longer mistaken for a caching failure.

= 1.1.8 =

**Combine CSS now also folds stylesheets printed in the `<body>` (Elementor/Astra/Gravity Forms)**

* **Page-builder stylesheets printed late in the `<body>` are now combined too.** Combine CSS only folded stylesheets in the `<head>`, but Elementor, Astra and Gravity Forms print widget/form CSS in the body, where it stayed as many separate render-blocking requests — adding network latency and, because that CSS styles above-the-fold elements (the nav menu, a contact form), causing layout shifts when it loaded after the content had already painted. Boost now folds those same-origin body stylesheets into a single bundle placed ahead of the content they style. It is intentionally kept render-blocking (not async, unlike the head bundle) precisely because it styles above-the-fold elements — so the menu and form are styled before first paint instead of flashing/reflowing. Fewer requests and steadier layout (CLS) on page-builder sites.

= 1.1.7 =

**Cloudways (managed Varnish) support + reverse-proxy bypass cookie**

* **New Varnish "Reverse Proxy Type" setting.** The existing Varnish integration assumed a self-managed Varnish whose VCL you control (PURGE for a URL, BAN for the whole site). Managed hosts like **Cloudways** don't let you edit the VCL and use a different purge protocol, so those purges silently failed and the reverse proxy kept serving stale HTML. Choose **Cloudways** under *Advanced → Purge Varnish* and Boost now purges the way Cloudways expects: `URLPURGE` for a single URL and a `PURGE` to `/?purgeall` for the whole zone, sent to the proxy front-end (defaults to `127.0.0.1:8080`). Purge requests to internal proxy ports are correctly sent over HTTP.
* **New `bcboost-nocache` bypass cookie.** Boost now sets this cookie for visitors who must always get a fresh page (logged in, or with an active WooCommerce cart) and clears it once they're anonymous again — emitting the cookie only on that transition so normal anonymous pages stay cacheable. Add `bcboost-nocache` to your reverse proxy's excluded-cookies list (on Cloudways: *Application → Varnish Settings → Excluded Cookies*) so the proxy bypasses its cache for those visitors. Boost's own page cache honours the same cookie.

= 1.1.6 =

**Fixed: Delay/Defer JS broke Elementor menus & widgets even after scripts loaded (no more excluding the whole Elementor stack)**

* **The order-safe loader now holds jQuery's `ready` until every delayed script has run.** Libraries like Elementor self-initialise on jQuery ready the instant their script executes. Because the loader runs delayed scripts one at a time, Elementor would initialise *before* later scripts (Elementor Pro's element handlers, SmartMenus, etc.) had registered — so dropdown menus and some widgets never bound their behaviour and appeared dead, even once everything had loaded. Boost now pauses jQuery ready the moment jQuery is defined and releases it only after the whole queue drains, so all components initialise together exactly as on a normal page load. This removes the need to exclude jQuery/Elementor from Delay JS to keep menus working — keeping the full Total-Blocking-Time benefit on Elementor sites.

= 1.1.5 =

**Fixed: Delay JS made the first click on menus/buttons do nothing (dropdowns appeared broken)**

* **The first click after a page loads now works with Delay JS enabled.** Delay JS holds scripts until the first user interaction, but that very interaction — usually a click on a navigation dropdown, accordion or button — was being "spent" loading the scripts and never reached the handler that had just loaded, so the element looked dead until you clicked a second time (most visibly: Elementor/SmartMenus dropdown menus wouldn't open). The loader now captures that first click, suppresses its default action so nothing navigates prematurely, and replays it on the original element once the scripts have loaded and bound their handlers — so a single click behaves exactly as it would without Delay JS. Normal links still navigate as expected.

= 1.1.4 =

**Fixed: Critical CSS was corrupted on save (broke hero/background images)**

* **Saving settings no longer escapes quotes in your Critical CSS.** WordPress adds slashes to submitted form data, and the settings handler stored the Critical CSS without removing them, so `background-image: url("…")` was saved as `url(\"…\")`. That is invalid CSS, so any rule relying on a quoted value — most visibly above-the-fold section/hero background images — silently failed to render once Critical CSS was enabled. Boost now unslashes the submitted settings before saving (the standard WordPress behaviour), preserving quotes and valid CSS escape sequences. If you enabled Critical CSS on 1.1.3 and a background went missing, re-save your settings on 1.1.4 and clear the cache.

= 1.1.3 =

**Faster LCP on page-builder sites: CSS background images now go through WebP + CDN, and the hero image is preloaded**

* **CSS `background-image` files are now served as WebP/AVIF and from your CDN.** Boost already rewrote `<img>` tags to modern formats and to the CDN host, but section/hero backgrounds set in CSS (the bulk of Elementor, Divi, Bricks, etc. layouts) were missed — they stayed as full-size origin JPG/PNGs baked into the combined stylesheet. Boost now rewrites those `url()` targets to the WebP/AVIF copy when one exists and to your CDN hostname, exactly as it does for images in the markup. On hero-heavy pages this can cut hundreds of KB off the single most important image. (Requires Combine CSS; clear the Boost cache after updating so the bundle is rebuilt.)
* **The hero / LCP background image is now preloaded with `fetchpriority="high"`.** A section background lives inside the CSS, so the browser cannot discover it until the stylesheet has downloaded and parsed — a major Largest-Contentful-Paint delay on page-builder sites. Boost now detects the first full-width section that carries a background image and adds a high-priority `<link rel="preload" as="image">` for it in the `<head>`, so it loads in parallel with the CSS instead of after it. Fully automatic, falls back to no preload when no hero is found, and can be turned off with the *Preload LCP image* option.

= 1.1.2 =

**Font display + safer Critical CSS**

* **`font-display: swap` is now applied to fonts inside your stylesheets.** Boost already added swap to Google Fonts links and inline `@font-face` blocks, but icon-font packs (Themify, Linearicons, ElementsKit, Font Awesome, …) declare their `@font-face` inside external stylesheets, where it was untouched. Boost now injects `font-display: swap` into those `@font-face` rules while minifying/combining CSS, so text and icons stay visible during font load (the "Font display" PageSpeed item). Governed by the existing *Optimize Google Fonts* toggle.
* **Critical CSS is no longer corrupted on save.** The Critical CSS field was sanitised with a text filter that strips every `%xx` URL-encoded sequence, which silently broke `url()` values (such as inline SVG data URIs) in pasted critical CSS. It now uses CSS-safe sanitisation that removes HTML tags but preserves valid CSS.

= 1.1.1 =

**Combine CSS &amp; Async CSS now work when a CDN is enabled (major mobile fix)**

* **Fixed: Combine CSS and Async CSS did nothing when BunnyCDN URL rewriting was on.** The CDN rewriter swaps every asset URL to your pull-zone hostname (e.g. `yourzone.b-cdn.net`) before Boost folds the page's stylesheets. Boost only recognised origin URLs, so it treated every CDN-hosted stylesheet as "external" and skipped it — leaving the whole page's CSS render-blocking even with Combine CSS + Async CSS (or the *Optimize for PageSpeed* preset) enabled. On a CSS-heavy Elementor site that left dozens of stylesheets blocking first paint and capped the mobile PageSpeed score. Boost now maps CDN asset URLs back to their local files, so all same-origin stylesheets are combined into one bundle and that bundle is delivered asynchronously — and the bundle itself is served from the CDN. This is the single biggest mobile First-Contentful-Paint / Largest-Contentful-Paint fix for sites running BunnyCDN. After updating, clear the Boost cache (and purge your BunnyCDN pull zone) so pages are rebuilt.

**WooCommerce: fixed "ghost products" added to the cart + safer real-time cart on cached pages**

* **Fixed: products silently appearing in the cart ("ghost products").** Boost's link prefetch (on hover/tap) and the Speculation Rules engine were speculatively loading WooCommerce "Add to cart" links — which are ordinary `?add-to-cart=ID` URLs marked `rel="nofollow"`. Loading one actually runs the action, so simply hovering or tapping near a product button could add it to the cart. Both engines now skip every `rel="nofollow"` link and any URL carrying a cart/AJAX/nonce action parameter (`add-to-cart`, `remove_item`, `undo_item`, `wc-ajax`, `add_to_wishlist`, `_wpnonce`), as well as the cart/checkout/my-account pages. Add-to-cart, remove and quantity changes now only happen when the shopper actually clicks.
* **Real-time cart keeps working with Delay JS.** The cart-fragments / add-to-cart scripts were already kept out of Defer/Delay so the mini-cart updates on cached pages, but jQuery — which they depend on — was still being delayed, so on a WooCommerce store those scripts could run before jQuery was ready. When WooCommerce is active Boost now also keeps jQuery running immediately, so the mini-cart, add-to-cart and quantity updates stay live. (On non-WooCommerce sites jQuery is still delayed for the Total-Blocking-Time win.)

= 1.1.0 =

**Order-safe JavaScript engine — fixes broken sliders/menus and unlocks a big PageSpeed gain**

* **Defer &amp; Delay JS no longer break themes (major fix).** Previously Boost only added `defer` (or the delay marker) to *enqueued* script files, but left the inline initializers a theme or page-builder hard-codes in the page running during parsing — before the libraries they call had loaded. On sliders, popups and many WPBakery/ThemeForest themes that meant `… is not a function` errors and broken layouts. Boost now treats inline and external scripts as one ordered unit: every eligible script is run through a single loader that executes them strictly in their original order, waiting for each external file to finish before the next. Inline initializers (RevSlider, WPBakery, theme setup) and a script's localized config keep working exactly as the theme intended.
* **Delay JS is now safe and on by default.** Because order is preserved, holding all JavaScript (including jQuery) until the first interaction — with a 5-second fallback for passive visitors — no longer breaks pages. This is the single biggest Total-Blocking-Time / PageSpeed lever and is now part of the recommended setup. After the delayed scripts run, Boost re-fires the page's `DOMContentLoaded` and `load` events so everything still initializes. Use *Delay JS Exclusions* to keep specific scripts (analytics, chat, etc.) running immediately, or add `data-no-delay` / `data-cfasync="false"` to a tag to opt it out.
* **"Optimize for PageSpeed" now includes Async CSS.** The one-click preset (Tools → *Optimize for PageSpeed*) now also enables Async CSS alongside Combine CSS so the combined stylesheet stops blocking first paint. Test your homepage after applying — themes that don't inline their above-the-fold CSS may briefly flash unstyled.
* **Async CSS now covers single-stylesheet sites too.** When Combine CSS has only one local stylesheet to work with, that sheet is now loaded asynchronously (with a `<noscript>` fallback) instead of staying render-blocking.

= 1.0.9 =

**Asynchronous CSS delivery — the mobile First Contentful Paint fix**

* **Combine CSS can now load asynchronously.** Combining stylesheets reduces the number of requests, but the single bundle was still render-blocking — on a CSS-heavy page-builder site that one large file can hold up first paint on a slow mobile connection. With *Async CSS (Lazy CSS)* enabled alongside *Combine CSS*, the combined bundle now loads without blocking the first paint (`media="print"` + onload swap, with a `<noscript>` fallback). The theme's inline above-the-fold CSS paints immediately and the bundle applies as soon as it arrives — and because the stylesheet is no longer hogging the connection, the LCP image can start downloading sooner too. This is the single biggest lever for mobile First Contentful Paint / Largest Contentful Paint on heavy Elementor/Divi sites. Enable both under File Optimization. (When Combine CSS is off, Async CSS continues to load each stylesheet asynchronously as before.)

= 1.0.8 =

**Faster Largest Contentful Paint + one-click PageSpeed setup**

* **Your hero image no longer waits for JavaScript (big LCP win).** Many themes and plugins ship their own JavaScript "lazy loader" that replaces every image — including your above-the-fold hero — with a tiny placeholder and only loads the real picture once scripts run. On a slow mobile connection that can push your Largest Contentful Paint many seconds later, and the browser's `fetchpriority="high"` hint is wasted on the placeholder. Boost now detects the LCP/above-the-fold image, restores its real source immediately (from the usual `data-src` / `data-orig-src` / `data-srcset` attributes), removes the lazy-load hooks so it can't be hidden again, and preloads it — using the responsive `imagesrcset`/`imagesizes` set when one is present so the browser fetches the exact size it will display. The hero starts downloading right away instead of after the page's JavaScript has executed.
* **Combine CSS now catches page-builder stylesheets (major fix).** Combine CSS previously merged files at the point WordPress builds the page, which runs *before* Elementor, Divi and similar builders register most of their stylesheets — so on those sites it folded only a handful of files and left dozens of render-blocking requests behind (and, worse, served the rest un-minified). Combine now runs on the finished HTML, so it reliably collapses *every* eligible same-origin stylesheet — including the late builder/plugin ones — into a single minified bundle, preserving cascade order. External stylesheets (Google Fonts, CDNs) and print/media-query sheets are left alone. This is the difference between "combine did almost nothing" and "70 requests become 1" on a typical Elementor site.
* **"Optimize for PageSpeed" one-click setup (new).** Tools → *Optimize for PageSpeed* applies our recommended high-impact configuration in a single click — page cache, CSS &amp; JS minify and combine, JavaScript defer plus delay-until-interaction, image lazy-load and WebP, GZIP and browser caching — then clears the cache so pages rebuild optimised. It's the fastest way to get a strong Core Web Vitals baseline without learning every individual toggle. Your CDN, Cloudflare, webhook and other credentials are left untouched, and you can fine-tune anything afterwards.

= 1.0.7 =

**Two features that silently didn't work now do**

* **Cache preloading now actually runs.** The preloader queued URLs and showed progress, but its background worker was never registered, so the crawl never started and progress sat at 0%. The batch-processing cron and its schedule are now booted on every request (including WP-Cron), so "Preload Cache" properly warms the site in the background.
* **Multisite: deleting a network site now clears its cache.** Cache is stored by URL, but deletion looked for a non-existent per-blog-ID folder, so a removed site's cached pages were left behind. Deletion now purges the cached pages for the site's actual domain and path.

= 1.0.6 =

**Critical CSS fix + automatic database cleanup**

* **Critical CSS toggle now works.** Pasting CSS into the Critical CSS box had no effect unless you also knew it was applied regardless of the on/off switch, while a second (per-URL) code path read a store that was never populated and did nothing. Now the *Enable Critical CSS* toggle properly controls whether your above-the-fold CSS is inlined, and the dead per-URL path has been removed.
* **Scheduled database cleanup (new).** Database → Automatic Cleanup can now run a safe, unattended cleanup on a daily or weekly schedule — post revisions, auto-drafts, spam comments, expired transients and orphaned post meta. Destructive/heavy operations (emptying trash, OPTIMIZE TABLE) are deliberately left to the manual buttons. Off by default.

= 1.0.5 =

**PageSpeed / Core Web Vitals improvements**

* **Self-host Google Fonts (new).** Boost can now download the Google Fonts stylesheet and its woff2 files to your server once and inline the localised `@font-face` CSS directly in the page. This removes the render-blocking request to `fonts.googleapis.com` and the extra DNS/TLS handshake to `fonts.gstatic.com` — both common PageSpeed Insights flags — and serves fonts from your own origin with `font-display: swap`. The download happens on a single cache-priming request and is reused for every visitor; the now-redundant Google Fonts preconnect hints are dropped automatically. Enable under Media → Google Fonts → *Self-Host Google Fonts*.
* **LCP image control (new).** A new “Never Lazy-Load These Images” list (Media → LCP / Above-the-Fold Images) lets you protect your hero/banner from lazy loading by URL fragment or CSS class, for the cases the automatic detection can’t infer.
* **Safer Delay JavaScript.** Delayed scripts now honour the `data-no-delay` attribute and the widely-used `data-cfasync="false"` opt-out, so scripts that must run immediately keep working.

= 1.0.4 =

**Geolocation / currency pricing now survives caching, plus WooCommerce and stability fixes**

* **Per-country & per-cookie cache variants.** The page cache used to store one frozen HTML file per URL, so a geolocation/currency switcher would "stick" on whichever currency the first visitor saw — e.g. a South African visitor being shown USD. Boost now stores a separate cached copy per visitor country and per currency cookie, so geo pricing keeps working while pages are still served from cache. Enable *Page Cache → Geolocation & Currency → Separate Cache per Country*; common currency-switcher cookies (Aelia, WOOCS, WPML, etc.) are handled automatically, and a new `BCBOOST_resolve_country` filter lets a theme supply the country when there's no GeoIP header. Variant responses are marked `private` so a CDN can't collapse them back into one.
* **WooCommerce cache exclusions now actually apply.** The `BCBOOST_cache_reject_uri` / `BCBOOST_cache_reject_cookies` / `BCBOOST_do_generate_cache` developer filters were documented but never executed, so WooCommerce's own cart/checkout/account exclusions silently did nothing. They are now enforced on the cache-write path.
* **WooCommerce mini-cart fix.** Cart-fragments and add-to-cart scripts are no longer deferred/delayed, so the mini-cart and cart count keep updating on cached pages.
* **Mobile cache actually works now.** The separate-mobile-cache option served mobile visitors but never wrote a mobile cache file, so mobile pages were re-rendered on every request. Mobile variants are now written and served correctly. Because most themes are responsive (identical HTML for both), separate mobile cache now defaults to **off** on new installs for a higher cache hit rate — turn it on only if you run a separate mobile theme or AMP. Existing sites keep their current setting.
* **Targeted purges now clear every variant.** Purging a URL removes all of its cached variants (desktop/mobile/country/cookie, plus their GZIP/Brotli copies) instead of a fixed handful of filenames.
* **HTML minifier can no longer blank a page.** On very large pages a single regex hitting PHP's `pcre.backtrack_limit` could return null and wipe the whole document. Every step now falls back to the original HTML, so a failed optimisation simply skips itself instead of breaking the page.
* **LCP fix — stop lazy-loading the hero image.** The lazy loader treated the first `<img>` in the DOM (usually the header logo) as the LCP, gave it `fetchpriority="high"`, and then lazy-loaded the *real* hero image even when the theme had already marked it `fetchpriority="high"` and preloaded it. It now respects an existing priority/eager image as the LCP, never lazy-loads it, doesn't steal priority for the logo, and won't emit a duplicate preload. Also fixed malformed `<img … / fetchpriority>` output from appending attributes after a self-closing slash.
* **Google Fonts hints no longer corrupted.** The optimizer was appending `?display=swap` to `preconnect`/`dns-prefetch` hints (e.g. `href="https://fonts.googleapis.com?display=swap"`). It now only rewrites the actual font stylesheet link.
* **Corrected nginx serving rules.** The recommended nginx snippet omitted the scheme directory (so it never matched the real cache files) and mishandled pre-compressed files (raw Brotli/GZIP served without `Content-Encoding`). The new snippet is variant-aware (mobile, currency/geo cookies, per-country), skips the cache safely for logged-in/cart/query-string requests, and uses `gzip_static`/`brotli_static` for correct compressed serving.

**Server cache & object cache compatibility (no clashes)**

* **Varnish / reverse-proxy support.** When enabled (Advanced → Server Cache & Compatibility), every BaseCloud Boost purge now also clears Varnish over HTTP — a `PURGE` for single URLs and a `BAN` for site-wide clears — so the proxy never serves stale HTML. Supports separate Varnish server addresses (preserving the site host in the `Host` header) and ships a ready-to-paste, ACL-restricted VCL snippet.
* **Object Cache Pro / Redis / Memcached: guaranteed coexistence.** BaseCloud Boost is a page cache and runs alongside a persistent object cache. It never installs a competing `object-cache.php` drop-in and never calls `wp_cache_flush()` (which would wipe the object cache on every page purge), and it registers its own group as non-persistent. The Advanced screen now detects and reports the active object cache backend.
* **Hosting auto-detection now actually runs.** The host detector (WP Engine, Kinsta, SiteGround, LiteSpeed, Cloudways, Pantheon, and more) was defined but never booted, so its host-specific tweaks did nothing. It now runs: on hosts that provide their own full-page cache (WP Engine, Kinsta, Pagely, Pantheon, WP.com VIP) BaseCloud Boost’s file cache disables itself to avoid double-caching, and host/LiteSpeed/SiteGround caches are purged alongside ours.

= 1.0.3 =

**Combine CSS is now safe — big win for Elementor and other builder sites**

* Combining CSS now rewrites relative `url()` and `@import` paths to absolute URLs. Previously, combining stylesheets into a single bundle could break web fonts (e.g. Font Awesome icons turning into boxes) and CSS background images, because their relative paths resolved against the bundle's location. This made Combine CSS unsafe on page-builder sites with many stylesheets — exactly where it helps most. It now works correctly, letting heavy Elementor/ElementsKit pages collapse dozens of render-blocking CSS requests into a handful.
* Per-file CSS minification also rewrites relative paths now, so individually minified stylesheets never lose their fonts or images either.
* Combining no longer folds `media="print"` (or other non-screen) stylesheets into the main bundle, so print styles can't leak onto the screen.

= 1.0.2 =

**Critical Bug Fix**

* Fixed a JavaScript minification bug that could break a site's scripts — most visibly leaving a theme "preloader"/loading screen spinning forever so the page never appeared. The minifier preserved quoted strings before stripping comments, so an apostrophe inside a code comment (e.g. "don't", "we're") was mistaken for the start of a string and swallowed real code, producing invalid JavaScript. The minifier is now a proper single-pass lexer that correctly understands strings, template literals, regular expressions, and comments, and never breaks Automatic Semicolon Insertion. Minify JavaScript is safe to leave enabled again.

= 1.0.1 =

**Performance**

* Major front-end speed improvements: cached the modern-image (WebP/AVIF) format lookups so pages no longer hit the disk for every image on every request.
* Removed a redundant full-page processing pass in the lazy loader — less PHP work per uncached request and faster Time to First Byte.

**Bug Fixes**

* Fixed "I cleared the cache but still see the old page." HTML pages were being sent to browsers with a long cache lifetime, so browsers (and shared caches) kept serving their own copy for hours and a server-side purge could not reach them. HTML is now always revalidated by the browser (a cheap 304 when unchanged), while static assets keep their long-term caching — so content edits appear immediately after a purge. Logged-in admins/editors now always see live, uncached content.
* The drop-in and .htaccess rules now self-heal on update: updating the plugin no longer leaves a stale `advanced-cache.php` behind, so fixes take effect without a manual deactivate/reactivate.
* Fixed lazy loading hiding images, iframes, and videos. The previous JavaScript placeholder approach could leave media permanently invisible if a script failed to run. Lazy loading now uses the browser's reliable native `loading="lazy"` and never hides content.
* The "Boost Cache" button in the admin toolbar no longer reloads you back to the dashboard. It now clears the cache instantly via AJAX with a rocket animation and keeps you on the current page.

**New Features**

* Image compression: BaseCloud Boost now generates high-quality WebP copies of JPEG/PNG uploads automatically. A new Bulk Compress tool on the Media screen compresses your entire existing media library in the background, with live progress. Originals are kept untouched, so quality and your media library are never broken.

**Security**

* API keys and secrets (Cloudflare API token, BunnyCDN API key, webhook secrets, PageSpeed Insights API key) are now encrypted at rest in the database using authenticated encryption.
* Secret fields are now masked in the admin UI and are never printed into page source.
* PageSpeed Insights audits now run through a secure server-side proxy, so your API key is no longer exposed to the browser.

= 1.0.0 =
* Initial public release.
* Full-page HTML caching with GZIP and Brotli compression variants.
* HTML, CSS, and JavaScript minification and combining.
* JavaScript defer and delay strategies.
* Critical CSS extraction and inline injection.
* Native lazy loading for images, iframes, and videos.
* WebP and AVIF next-gen image serving.
* YouTube and Vimeo video facade (click-to-play thumbnails).
* Sitemap-based cache preloader with real-time progress tracking.
* Cloudflare API cache purge integration.
* BunnyCDN API cache purge integration and URL rewriting.
* Generic CDN hostname rewriting.
* Database optimizer (revisions, transients, orphaned postmeta).
* Security headers module (HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy).
* Heartbeat API throttle to reduce admin-area server load.
* Performance metrics webhook for external monitoring.
* WP-CLI commands for cache management.
* WordPress Multisite support.
* WooCommerce cart and checkout cache exclusion.
* Google Fonts optimization with font-display=swap.

== Upgrade Notice ==

= 1.3.6 =
New built-in Critical CSS generator (Beta, opt-in): create per-template above-the-fold CSS in one click — no external tooling — then choose yourself whether to enable Critical CSS inlining and Async CSS. Off by default; nothing changes until you switch it on.

= 1.3.5 =
Mobile speed release: hero image downloads start sooner (auto-preconnect), above-the-fold images are never lazy-loaded, lazy images download at their true rendered size, and icon fonts no longer flash placeholder glyphs. Plus a refreshed admin look.

= 1.3.4 =
Fixes corrupted WebP/AVIF conversions (interrupted write or faulty server encoder) being
served as broken/blank images: conversions are now written atomically and must fully decode
before use, with the original image always served as the fallback. After updating, run
`wp bcboost verify-conversions --delete-broken --regenerate` (or re-run Bulk Compress) to
repair existing conversions, then clear the cache. Update recommended.

= 1.3.3 =
Hero-image detection fixes: images with 4-digit widths (e.g. width="1000") lazy-load again
and can be the preloaded hero, theme background bands and thumbnails are no longer preloaded
as the hero, and detection now reads the page's own inline styles. Clear the cache after
updating. Update recommended.

= 1.3.2 =
Hotfix: WebP/AVIF serving could display the wrong image when a separately-uploaded media file shared a filename with an image Boost optimized. Boost now verifies a converted file is a real conversion before serving it. Affected pages self-correct after a cache clear. Update recommended.

= 1.3.1 =
Hotfix for 1.3.0 on Redis/persistent-object-cache hosts: settings saves could appear not to stick (values were saved in the database but the object cache kept serving old ones). Self-heals on the first wp-admin load after updating. Update recommended.

= 1.0.3 =
Combine CSS is now safe for Elementor/page-builder sites (relative font and image paths are rewritten correctly). Recommended if you want to cut the number of CSS requests.

= 1.0.2 =
Important fix: JavaScript minification could break theme scripts and leave a loading/preloader screen spinning so the page never rendered. Update strongly recommended.

= 1.0.1 =
Fixes "cleared cache but still see the old page" (HTML is no longer over-cached by browsers) and lazy loading that could hide images/videos. Adds automatic image compression with a bulk tool, makes pages faster, encrypts your API keys, and turns the toolbar Boost Cache button into an instant in-place clear. Recommended for all users.

= 1.0.0 =
Initial public release. No upgrade steps required.
