Documentation
Install Speed of Light and let it tune itself — or take full manual control. Everything from set-and-forget defaults to WP-CLI and the REST API is here.
Requirements: WordPress 6.0+ · PHP 7.4+ · write access to wp-content/.
On this page
Getting started
Speed of Light (Pro) is an edge-aware HTML caching engine that serves your pages as ready-made static files, so visitors get the fastest possible response without breaking sessions, layouts, or shared-hosting limits. It works on any WordPress site and is especially well suited to block themes. This guide walks you through requirements, installation, activating your license, and getting cached in a few minutes.
Speed of Light caches anonymous visitors only. Logged-in users, WooCommerce carts, checkout, admin screens, REST requests, POST submissions, and any request carrying a recognized session cookie are automatically bypassed and served fresh — they are never stored in the cache. There are no per-user or personalized cache copies to manage; the cache is a single shared, anonymous layer. The bypass list ships with 20+ known session/login cookie patterns out of the box.
Requirements
Before installing, confirm your environment meets these minimums:
- WordPress 6.0 or higher (tested up to the current release).
- PHP 7.4 or higher. If PHP is older than 7.4, the plugin refuses to load and shows an admin notice instead of activating, so it can never run on an unsupported version.
- A valid Speed of Light (Pro) license, delivered through Freemius when you purchase.
- Write access to
wp-content/andwp-config.phpso the plugin can install its cache drop-in and enable theWP_CACHEconstant automatically. On most hosts this already works; see the note in Step 4 if yours restricts file writes.
Installation and setup
Install the plugin
You can install Speed of Light the same way as any premium WordPress plugin, using whichever method suits your workflow:
Upload the zip (recommended): In your WordPress admin, go to Plugins → Add New → Upload Plugin, choose the speed-of-light-premium.zip file you downloaded, then click Install Now.
Or drop the folder manually: Unzip the package and upload the speed-of-light folder into wp-content/plugins/ over SFTP or your host file manager:
# Upload the unzipped plugin folder into your plugins directory
wp-content/plugins/speed-of-light/ Do not run more than one full-page cache plugin at the same time — remove or deactivate any other page-cache plugin first to avoid conflicts.
Activate and connect your license
Open Plugins and click Activate under Speed of Light (Pro). A new SOL menu item appears in your admin sidebar. Go to SOL → Dashboard and follow the license connection prompt to sign in with the email you used at purchase. Enter your license key when asked; this links the plugin to your account through Freemius, unlocks automatic updates, and enables the cloud-assisted optimizations.
The license activation is what turns on automatic updates and the optional cloud features. Everything else — the local page cache and core optimizations — begins working the moment the plugin is active.
Let Smart Configuration apply safe defaults
On first activation, Speed of Light runs its Smart Configuration engine. It profiles your environment (hosting, existing cache layers, server capabilities) and automatically applies only the universally-safe subset of recommended optimizations — once, so it never fights choices you later make. This is a genuine set-and-forget setup: you do not pick a "mode." There is no global one-click/aggressive switch to reason about.
Riskier, site-specific optimizations (such as Remove Unused CSS, Critical CSS, or AVIF image conversion) are deliberately left off and surfaced as clearly labeled "Recommended" hints, so you opt into them when you're ready. If Speed of Light detects a conflicting full-page cache plugin, it safely skips auto-applying defaults until the conflict is resolved.
Where individual features do offer intensity levels, they are per-feature and self-explanatory — CSS/asset performance runs at off / safe / balanced / aggressive / custom, and JavaScript delay runs at safe / aggressive / custom. The defaults chosen for you are conservative.
Confirm the local page cache is live
Full-page local caching is the heart of the plugin and is enabled automatically. On activation, Speed of Light writes its cache drop-in to wp-content/advanced-cache.php and sets the WP_CACHE constant in wp-config.php, so cached pages are served before WordPress even fully loads. There is nothing to switch on to start caching.
To review cache behavior — expiry, preloading, and URL/cookie exclusions — open SOL → Dashboard and go to the Local Cache section. Visit your site in a logged-out (incognito) browser window and reload once; the second load is served from the static cache. You can clear the cache at any time from the SOL menu.
If wp-content/ or wp-config.php is not writable on your host, the drop-in or WP_CACHE constant may not install and pages won't be cached. Grant write access (or ask your host) and re-save from the Local Cache section, or deactivate and reactivate the plugin to retry the automatic setup.
What happens next
With the plugin active and your license connected, Speed of Light is already caching anonymous traffic and applying its safe defaults. From here you can explore optional wins at your own pace: enabling the persistent Redis object cache, self-hosting Google Fonts, delaying non-critical JavaScript, and per-feature CSS optimization. None of it is required — the plugin is fast and safe as installed.
Every plan is the complete plugin. There is no "lite" tier or feature gate between plans — you always get the full Speed of Light (Pro) feature set. Plans differ only by the number of sites you're licensed for and your priority support level. Upgrading or downgrading never adds or removes features. Questions? Reach us at [email protected].
Smart Configuration
Speed of Light is built around a simple promise: you shouldn't need to be a performance engineer to run a fast site. Smart Configuration is the intelligence layer that makes that possible. When the plugin is activated, it looks closely at your specific hosting environment, works out which optimizations are genuinely safe for your setup, and switches on that safe set automatically — once. Everything else is offered to you as a clearly-explained, one-click suggestion rather than being forced on.
This is a set-and-forget design. There is no big "mode" switch to agonize over, no "Basic / Aggressive / Developer" preset to pick from, and no risk of the plugin silently changing a setting after you've made a deliberate choice. The rest of this section explains exactly what it detects, what it turns on for you, and how to stay in full control.
There is deliberately no single global "performance level" selector. Speed of Light doesn't ask you to gamble on one master dial. Instead, each feature is tuned individually, and the couple of features that do have intensity levels (CSS/asset optimization and JavaScript delay) have their own per-feature Safe / Balanced / Aggressive control, described near the end of this section.
The Environment Profiler
Before recommending anything, Speed of Light builds a factual picture of your site — its environment profile. This step is strictly read-only: the profiler never changes a setting, it only observes. It's the honest, up-to-date basis every recommendation is reasoned from.
The profile is computed automatically, cached for about 12 hours, and stored without slowing down your front-end pages (it's only ever read in the admin area, never on a cached visitor page load). Here's what it looks at:
Server and runtime
- Web server software — whether you're on LiteSpeed, NGINX, or Apache.
- PHP version and WordPress version.
- OPcache — whether PHP's compiled-code cache is available and running.
- Multisite — whether this is a WordPress network install (some features behave differently on networks).
Compression
The profiler checks whether your server can produce Brotli and/or gzip compressed responses. Brotli produces noticeably smaller pages, so when it's available the plugin will tell you so and prefer it for compressing cached pages.
Object-cache backend
It detects whether a persistent object cache is already active and, if so, what kind — Redis, Memcached, or something else. It also separately checks whether your server could run Redis even if WordPress isn't using it yet, so the plugin can point you toward enabling its own Redis-backed object cache when the server supports it.
Image capabilities
Image conversion is only useful if your server can actually produce the modern formats, so the profiler inventories:
- Whether Imagick and/or GD are installed (and their versions).
- Whether your server can encode WebP and AVIF.
- Whether command-line encoders (
cwebp,avifenc) are present, when the server allows running them.
Cloudflare in front of your site
Speed of Light detects Cloudflare two ways. First, whether you've connected Cloudflare credentials in the plugin. Second — and independently — whether Cloudflare is proxying your traffic right now, spotted from the tell-tale headers Cloudflare adds to every request it forwards. Because an admin or background request occasionally doesn't carry those headers, the plugin remembers a positive sighting so it doesn't flip-flop on you.
Speed of Light's edge integration is Cloudflare-only, and it uses Cloudflare's modern Cache Rules. If Cloudflare is detected in front of your site but not yet connected in the plugin, you'll get a friendly notice inviting you to connect it in the Edge Cache section to unlock edge caching and 103 Early Hints.
Managed hosting
Many managed hosts run their own full-page cache. The profiler recognizes a range of them by their stable platform signatures — Kinsta, WP Engine, SiteGround, SpinupWP, Pantheon, WordPress.com / WP Cloud, GridPane, RunCloud, and Cloudways. When your host already provides a server-level page cache, Speed of Light knows not to stack a redundant caching layer on top, and instead focuses on the optimizations and Cloudflare edge caching that add real value there.
Site shape
It notes whether you run WooCommerce or Easy Digital Downloads, whether the site is multilingual (WPML, Polylang, or TranslatePress), and whether comments are open. These shape which optimizations are recommended and how cautiously — e-commerce and page-builder sites, for example, are treated as more script-sensitive.
Conflicting and related plugins
Finally, it reuses the plugin's compatibility report to identify your active theme, any page builders, and — critically — any other caching or optimization plugins that would fight with Speed of Light. This is what prevents the plugin from, say, minifying your CSS when another plugin is already doing it.
The Recommendation Engine and Safe Auto-Apply
Once the profile is built, the recommendation engine maps it to a list of concrete settings. Every recommendation comes with a plain-language reason you can actually understand — not jargon. Each one is also tagged as either safe to apply automatically or suggest-only (something you should review and opt into yourself).
What gets turned on automatically on a fresh install
On first activation — and only once — Speed of Light applies the universally-safe subset for your environment. Typically that includes:
- Self-host Google Fonts — serves fonts from your own server so the browser skips a slow third-party request.
- Preload fonts — loads your main fonts early so text appears without a flash or delay.
- Lightweight video embeds — shows a fast preview image for YouTube/Vimeo and only loads the real player when clicked.
- Self-host third-party scripts — copies common external scripts (like analytics) to your server to cut outside requests.
- Compress cached pages — sends smaller pages to visitors (using Brotli when your server supports it).
- Serve images as WebP — when your server can produce WebP, an existing smaller WebP version is served automatically. (WebP works on every modern phone.)
- Minify CSS and JavaScript — but only when no other optimization plugin is already doing it.
- Cache commenter avatars — when comments are open, Gravatar images are stored locally so comment sections load without external requests.
The engine never picks a fight with your environment. If another optimizer (for example Autoptimize) is already minifying your CSS and JavaScript, Speed of Light will not also enable minification — running both can break your styles or scripts. In that case it explains why the toggle is being left off, naming the plugin that's already handling it.
What is suggested but never auto-applied
Higher-impact optimizations that can affect how your pages look are deliberately left as suggestions you opt into after reviewing — they are never switched on silently. These include:
- Remove Unused CSS — a strong loading-speed win, but because it strips styles it stays a suggestion. It's recommended on for straightforward sites (with a nudge to check your key pages afterward), and recommended off when a page builder is present or another plugin is already optimizing CSS.
- Delay JavaScript — always recommended, but on page-builder and WooCommerce sites (the most timing-sensitive) it's offered as a one-click opt-in rather than applied automatically on a fresh install.
- 103 Early Hints — recommended when Cloudflare is in front of your site (it needs Cloudflare to take effect), and otherwise explained as not applicable.
- AVIF image format — WebP is always the recommended default because it's universally supported. AVIF is smaller but can break on older iPhones, so it stays strictly opt-in.
When auto-apply is skipped entirely
If the profiler finds a conflicting full-page caching plugin is active, Speed of Light does not auto-apply anything. Stacking two full-page caches is the wrong fix — the right move is to deactivate the other plugin. You'll see a clear notice telling you exactly that. Importantly, the plugin does not burn its one-time "smart defaults applied" marker in this case, so once you resolve the conflict and it's activated cleanly, the safe defaults can still apply for you.
The Override Guard — your choices are permanent
This is the piece that makes set-and-forget trustworthy. The moment you change a setting by hand, Speed of Light records it as yours and stops auto-managing it. From then on, the engine will never re-apply, revert, or overwrite that setting — not on a re-scan, not on an upgrade, not ever.
A worked example:
You choose AVIF
Speed of Light recommends WebP as the image format. You deliberately switch it to AVIF because you know your audience.
Your choice is remembered
That setting is added to your personal overrides list. It's now flagged as user-controlled, not engine-controlled.
A re-scan happens later
Even though the engine would still "prefer" WebP for safety, it sees your override and leaves AVIF exactly as you set it. No silent switch-back.
In the admin interface, each setting the engine has an opinion about shows its recommendation and a plain-language reason. Settings you've personally changed are marked as overridden, so it's always obvious which choices are yours and which are the plugin's defaults.
Per-feature manual levels (Safe / Balanced / Aggressive)
For the two areas where a single on/off switch isn't expressive enough, Speed of Light gives you a per-feature intensity control — always optional, always yours to set:
- CSS / asset optimization has a performance mode of
off,safe,balanced,aggressive, orcustom. Higher levels squeeze out more speed but touch more of your styling, so you can dial in exactly how far you want to push. - JavaScript delay has its own mode of
safe,aggressive, orcustom. Safe mode only delays known third-party scripts (the low-risk ones); aggressive holds more scripts back for a bigger responsiveness win on interaction.
These are the only intensity levels in the plugin, and each lives with its own feature — there is no global equivalent. Once you set one of these levels yourself, the override guard protects it just like any other manual choice.
Re-scanning when your site changes
Your environment isn't frozen, so the profile isn't either. Speed of Light refreshes automatically about every 12 hours, and it also re-scans immediately when something that matters changes, so its advice never describes a state you've already moved past. It re-profiles right away when:
- You activate or deactivate a plugin (a new conflict may have appeared or been resolved).
- You save Cloudflare credentials (edge features may now be available).
A theme switch or a core/theme/plugin update is also treated as a "redesign" event: because your markup and stylesheets may have changed, Speed of Light automatically clears any generated Critical CSS, Used CSS, and cached optimization plans so they regenerate against the new design — while keeping your page cache warm.
Re-scanning updates the plugin's advice, and it may auto-apply newly-safe defaults on a clean re-activation — but it will never touch a setting you've overridden by hand. If you deliberately turned something off, it stays off. If a re-scan ever surfaces a fresh recommendation, it appears as a suggestion for you to accept, not as a change made behind your back.
In short
- Speed of Light profiles your real environment before recommending anything, and never changes a setting just to observe it.
- On first activation it turns on only the universally-safe wins for your setup — and skips even that if a conflicting cache plugin is active.
- Bigger, style-affecting optimizations are always offered as reviewable one-click suggestions with a reason, never forced.
- Any setting you change by hand is remembered and protected forever by the override guard.
- The only intensity levels are per-feature (CSS/assets and JS delay) — there is no global mode to get wrong.
Page cache
Speed of Light's page cache turns each of your pages into a ready-made static HTML file on disk. Once a page is cached, a returning anonymous visitor is handed that file almost instantly, so most of the usual work — loading WordPress, running plugins, querying the database, rendering the theme — is skipped entirely for that request. This is the single biggest speed win the plugin delivers, and for the vast majority of sites it works perfectly with no configuration at all.
Full-page caching is always on and set-and-forget. There is no switch to flip and nothing to tune to get the core benefit — the plugin starts caching the moment it is active. Everything below is about the (few) cases where a page should not be cached, and how to control them.
How a cached page is served
When caching is active, Speed of Light installs a small drop-in file (advanced-cache.php) into your wp-content folder and sets WordPress's WP_CACHE flag. Because of where WordPress looks for this file, it runs before WordPress itself finishes loading. That means a cache hit can be answered without booting the full WordPress core, most plugins, or the theme — which is why cached responses are so fast.
On each request the drop-in:
- Works out which cached file matches this exact request (see Cache variants below).
- Checks that a valid, non-expired file exists for it.
- Confirms the request is a normal, anonymous page view that is safe to serve from cache.
- If all of that holds, sends the stored HTML straight to the browser and stops — WordPress never fully loads for that visit.
Served responses carry an X-Cache-Plugin: SpeedOfLight.Pro header so you can confirm a page came from the cache. If no valid cached file exists, the request falls through to WordPress as normal, the page is rendered once, and the result is stored for next time.
Atomic, corruption-safe writes
Every cached file is written atomically: the HTML is first written to a temporary file, its size is verified, and only then is it renamed into place. If anything fails partway through, the temporary file is discarded and no cache file is created. Visitors are therefore never served a half-written or truncated page — they either get a complete cached file or a freshly rendered one.
Gzip and Brotli ready
When compression is enabled, Speed of Light does not just cache the plain HTML — at the moment a page is cached it also pre-compresses it and stores the compressed copies alongside the original. A gzip (.gz) copy is written whenever gzip is available (effectively everywhere), and a Brotli (.br) copy is written when your server has the Brotli extension. Because the heavy compression work happens once, at cache time, rather than on every request, visitors whose browsers accept compression are handed the pre-built compressed bytes with the correct Content-Encoding header, and the plain uncompressed file always remains as a universal fallback for any browser.
Cache variants
The same URL can legitimately need to be served differently to different visitors. Speed of Light handles this by storing separate cached files — variants — keyed on a handful of request characteristics, so each visitor receives the correct version while still getting a cache hit. Variants are created automatically as needed:
- Scheme —
httpandhttpsrequests are cached separately. - Language — on multilingual sites running Polylang or WPML, the active language (read from the language cookie) is part of the key, so
/about/serves the correct translation. - Device — when the Mobile Cache option is on, mobile visitors (detected from the browser's User-Agent) get a separate
-mobilevariant, so a mobile-specific render is never served to desktop or vice versa. - WebP — when WebP image conversion is enabled, browsers that advertise WebP support get a WebP-optimized variant of the HTML.
- Encoding — Brotli and gzip copies are stored as separate
.br/.gzfiles next to the plain one.
Variants are anonymous. They vary the page by request characteristics (scheme, language, device, WebP, encoding) — never by who the visitor is. There is no per-user or personalized caching: logged-in and other individualized requests are simply not cached at all (see below).
What is never cached
Caching is strictly for anonymous, public page views. Any request that is personalized, transactional, or otherwise unsafe to store is automatically bypassed — the page is rendered fresh by WordPress every time and never written to disk. You do not need to configure any of this; it is built in.
Logged-in and personalized sessions
Speed of Light inspects the request's cookies and bypasses the cache whenever it sees a sign of a logged-in user, a commenter, or an active store session. The built-in bypass list covers 20+ cookie patterns, including:
- WordPress login and auth cookies (
wordpress_logged_in,wordpress_sec,wordpress_no_cache). - Password-protected-post cookies (
wp-postpass). - Comment-author cookies for people who have left comments (
comment_authorand related). - WooCommerce cart and session cookies (
woocommerce_items_in_cart,woocommerce_cart_hash,wp_woocommerce_session_, and other WooCommerce cookies). - Easy Digital Downloads cart and session cookies (
edd_items_in_cartand other EDD cookies). - A range of other commerce and session cookies (YITH wishlist/session, Ecwid, Bookly, WP eCommerce, and more).
The practical effect: shopping carts, checkouts, "my account" pages, and any signed-in dashboard are never served stale from cache — each of those visitors always sees a live, correct page. Because these sessions are bypassed rather than stored, a customer with items in their cart can never be shown someone else's cart.
Request types that are never cached
Independently of cookies, a request is excluded when any of the following is true:
- The request method is not
GET(so form submissions and otherPOSTrequests are never cached). - The response is not a normal
200 OK— including 404 pages, even when a custom 404 template returns a 200 status. - It is a WordPress admin (
wp-admin), login, or admin-context request. - It is a REST API request (
/wp-json/) or an XML-RPC request. - It is a feed, trackback, robots, or preview request.
- It is a site search results page.
- The page is password-protected and the password has not been entered.
- The browser is not asking for an HTML document.
Only complete, valid HTML documents are ever stored. If the output is not recognizable HTML (for example a JSON or XML response, or an XSL-styled document), it is left uncached.
Excluding specific pages
Beyond the automatic rules, you can tell Speed of Light to never cache particular pages of your own. These controls live on the Exclusions settings. An excluded URL is skipped by the entire pipeline — it is neither cached nor rewritten, and is served exactly as your theme renders it. You can exclude by:
- Post ID — one or more specific post or page IDs.
- Post type — every entry (and its archive) of a chosen post type.
- Page path / URL — path patterns, one per line. A trailing
*acts as a wildcard, so/form*matches every path that begins with/form. Sensible defaults are already in place for common non-cacheable areas such as/cart,/checkout,/my-account, and login/registration paths. - Query string — request parameters that should force a bypass.
- Cookie — additional cookie-name patterns (added on top of the built-in 20+) that should bypass the cache when present.
Common marketing and tracking parameters — such as utm_source, gclid, fbclid, and pagination parameters — are treated as cacheable by default, so a shared campaign link still benefits from the cache instead of forcing a miss on every visit.
Excluding a page from your own code
Developers and other plugins can force any request to bypass the page cache by defining the standard DONOTCACHEPAGE constant while the page is being generated. When it is defined and truthy, the current page is not stored:
// In a template, plugin, or mu-plugin, while the page is rendering: if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); }
For finer control you can hook the sol_bypass_cache filter. It runs on every request and receives the plugin's own decision (already true for anything excluded above); return true to bypass the cache for the current request, or false to allow it:
// Bypass the page cache whenever a "no_cache" query flag is present. add_filter( 'sol_bypass_cache', function ( $bypass ) { if ( isset( $_GET['no_cache'] ) ) { return true; // never cache this request } return $bypass; // otherwise keep the plugin's own decision } );
These two hooks affect only whether a page is cached. They do not touch WordPress user sessions or any private data — an excluded page is simply rendered live each time it is requested.
Cache lifetime and automatic clearing
Speed of Light keeps cached pages fresh in two complementary ways.
Clear on content change (default)
Out of the box, the cache is kept fresh by events rather than by a timer. When you make a change in WordPress, the affected cached pages are cleared automatically and re-built, so visitors see updates promptly without you ever touching a "clear cache" button. Clearing happens when, for example:
- A post, page, or product is published, updated, or deleted — its own page is refreshed, along with the pages it appears on: the blog index, its post-type archive, the category/tag and other term archives (including parent terms), author archives, and date archives.
- A comment is posted, edited, approved, unapproved, or deleted — the page it belongs to is refreshed.
- A term (category, tag, or custom taxonomy term) is added, edited, or deleted — its archive pages are refreshed.
- Key site options change — such as the site title/tagline, permalink structure, front-page/blog-page assignment, active theme, or widgets.
- WooCommerce product stock changes — the product's page is refreshed.
On multilingual sites, editing a post also refreshes the linked translations in every language, so all versions stay in sync. To avoid needless churn, repeated saves of the same item within a short window are debounced (grouped together) rather than clearing the cache over and over.
Because the default behavior clears exactly the pages a change affects and rebuilds them, the recommended cache lifetime is Never (no time-based expiry). Pages then stay cached — and fast — until something actually changes, at which point the right pages refresh on their own.
Time-based expiry (optional)
If you prefer a fixed lifetime, you can set a cache expiry time in hours. A value of 0 means Never — pages do not expire on a timer and are only refreshed by the content-change events above (the default). When you set a non-zero number of hours, any cached file older than that is treated as expired: the next visitor triggers a fresh render, and the stale file is replaced. Expiry and content-change clearing work together, so even with a timer set, edits still refresh the relevant pages immediately rather than waiting for the timer to run out.
Clearing the cache manually
You can always clear the cache yourself — for a single page or for the whole site — from the plugin's controls and the WordPress admin bar. A full site clear also asks supported managed hosts and, when configured, the Cloudflare edge to purge their own copies, so every layer stays consistent and visitors are not left seeing an old page from a cache further downstream.
If you have edited a page directly in the database, or made a change through a route WordPress does not fire a hook for, the automatic clearing cannot know about it. In those cases, trigger a manual clear so the affected pages are rebuilt.
Object cache (Redis)
Object caching is optional. It is a bonus layer on top of Speed of Light's page cache, not a requirement. The page cache already serves your visitors fully-built HTML. The object cache helps the parts of WordPress that still run — the admin area, logged-in users, WooCommerce, search, and any page WordPress builds fresh — by remembering repeated database lookups. If you do not have a Redis server, you can safely leave this feature off and lose none of the page-caching speed.
The object cache is a separate, complementary layer to the page cache. It is worth understanding the difference:
- The page cache stores the finished HTML of a whole page and serves it to anonymous visitors without running WordPress at all. It is what makes cached pages feel instant.
- The object cache stores the results of individual database queries — option values, term lookups, post metadata, user data, WooCommerce fragments, and anything a plugin asks WordPress to remember. WordPress normally throws these results away at the end of every request and re-queries them on the next one. With a persistent object cache, those results survive across requests, so the same query does not hit MySQL over and over.
Where the page cache speeds up cached pages, the object cache speeds up everything the page cache cannot help: the WordPress admin, logged-in sessions, AJAX, REST calls, cart and checkout, search results, and the first (uncached) visit to any page. On a busy or database-heavy site the difference is large; on a small brochure site it may be modest — which is why it is optional.
What powers it
Speed of Light ships its own native Redis object-cache engine. Redis is a fast in-memory data store that runs on your server (or a nearby managed instance). When you enable object caching, the plugin installs a small file called object-cache.php into your wp-content folder. WordPress calls this file a drop-in: it replaces WordPress's default throw-it-away cache with our Redis-backed one for every request, front-end and admin alike.
The drop-in is our own code — it is stamped with our name and version so the plugin can recognise it, keep it up to date, and remove it cleanly. It works with the phpredis PHP extension when available, and falls back to the pure-PHP Predis library if that is all your host provides.
This is a persistent object cache in the WordPress sense: cached data persists in Redis across requests. It opens a fresh Redis connection each request rather than holding a pooled or persistent connection, which keeps behaviour predictable across PHP workers and restarts.
Enabling object caching
Before you enable it, your server needs a running Redis instance and a Redis client for PHP (either the phpredis extension or the Predis library). Most managed and performance hosts include Redis; if you are unsure, ask your host or run the built-in connection test described below. If no Redis client is present, the plugin tells you so and refuses to install the drop-in rather than break your site.
Confirm Redis is available
Open the Object cache (Redis) section. The status panel shows whether a Redis client library was detected on your server. If it reports none, install Redis (or ask your host) before continuing.
Enter your connection details
The defaults — host 127.0.0.1, port 6379, database 0 — suit most single-server setups where Redis runs locally. For a remote or managed Redis, fill in the host, port, database number, and password. You can also choose a connection scheme: standard TCP, encrypted TLS, or a Unix socket (enter the socket path). The password field is write-only: once saved it is never sent back to your browser, and leaving it blank on a later save keeps the stored password unchanged.
Test the connection
Use Test connection to check your settings against Redis without touching the live cache. A successful test reports the round-trip latency in milliseconds, the transport it used (Unix socket / TCP / TLS), the Redis server version, and its current memory use. If it cannot connect, the message tells you why so you can correct the host, port, password, or firewall.
Enable the drop-in
Turn object caching on. The plugin installs object-cache.php, writes your connection settings to a protected config file, and mirrors the non-secret parts into wp-config.php inside a clearly-marked, auto-managed block. From the next request onward, WordPress serves object-cache lookups from Redis.
The drop-in is written safely. It is created via a temporary file and atomically renamed into place, so a visitor can never load a half-written file. If another object-cache plugin already owns object-cache.php, Speed of Light refuses to overwrite it and asks you to remove the other one first — it will never clobber a foreign drop-in or delete one that is not ours.
Where your Redis password is stored
Your connection settings are written to a generated config file in wp-content that the drop-in reads first. Because that file can contain the Redis password in plain text, the plugin restricts it to owner-only permissions (0600) so other tenants on shared hosting cannot read it. The non-secret settings (scheme, database, prefix, and — only for a genuinely remote endpoint — host and port) are also mirrored into wp-config.php as SOL_REDIS_* constants; the password is deliberately never written there. For a hardened setup you can instead define WP_REDIS_PASSWORD in wp-config.php yourself — the drop-in honours it.
Value compression
Larger cached values (serialized arrays, option blobs, rendered fragments) can be compressed before they are stored in Redis, which reduces Redis memory use and network traffic. Compression is on by default and fully automatic. The engine picks the best codec your server actually has, in order of preference:
- zstd — the preferred codec: excellent ratio and very fast.
- lz4 / lzf — extremely fast codecs with a good ratio.
- gzip — the universal fallback, available almost everywhere.
You can leave the codec on Auto (recommended) to let the plugin choose, pin a specific codec, or turn compression off. Only values above a minimum size threshold (1 KB by default) are compressed, and a value is only stored compressed when the compressed form is genuinely smaller — otherwise it is stored as-is. Each compressed value carries a tiny tag identifying which codec wrote it, so if you change the codec later, older values still decompress and read correctly. If a codec's extension is ever missing at read time, that value is simply treated as a cache miss and WordPress re-fetches it — never a fatal error.
igbinary serialization
Before a value goes into Redis it must be serialized into a string. If the igbinary PHP extension is present, the drop-in uses it automatically; otherwise it falls back to PHP's built-in serializer. igbinary produces smaller, faster-to-read output than PHP's default, so where your host provides it you get a free efficiency gain with no configuration. Serialization is always symmetric and defensive: a value that cannot be serialized (such as a closure or a database handle) is skipped rather than allowed to crash the site, and an unreadable stored value is treated as a miss so WordPress refetches it.
Live diagnostics
Once the cache is running, the section shows live figures pulled from the active drop-in and from Redis itself, giving you the same visibility you would expect from a dedicated object-cache tool:
- Hit ratio, plus raw hits and misses for the current request — how often WordPress found what it needed already cached.
- Backend and status — whether it is genuinely connected to Redis or running in the in-memory fallback described below.
- Transport — the actual live endpoint in use (Unix socket, TCP, or TLS), which reflects any automatic socket upgrade the drop-in made.
- Client and serializer — phpredis vs. Predis, and igbinary vs. PHP.
- Compression — the active codec, the codecs available on your server, and the space saved this request.
- Redis server health — version, memory used, the
maxmemorypolicy, evicted keys, expired keys, uptime, connected clients, instantaneous operations per second, keyspace hits/misses, and the number of keys in your database.
Watch evicted keys and the maxmemory policy. A steadily climbing eviction count means Redis is full and is discarding cached data to make room — a sign your Redis instance may need more memory or a different eviction policy. The diagnostics also surface the last connection error (for example an authentication failure) so a problem reads as a clear cause rather than just "not connected".
Multisite: per-blog isolation
On a WordPress multisite network there is a single, network-wide object-cache.php drop-in and one shared connection configuration (stored as a network option). Even so, each site in the network is isolated inside Redis: cache keys are namespaced per blog, so one site can never read or overwrite another site's cached data. Global, network-level data (such as network-wide user and site records) is shared where WordPress expects it to be, exactly as core does. When you flush the cache, the plugin scans and removes only its own prefixed keys — it never touches other applications that happen to share the same Redis database.
Because the drop-in and its connection settings are network-wide, object-cache configuration on multisite is managed once for the whole network rather than per-site.
Graceful fallback if Redis goes down
A Redis outage must never take your site down, and it doesn't. If Redis is unreachable — the server is stopped, a socket is blocked, authentication fails, or the network drops — the drop-in silently falls back to a per-request in-memory cache. Your site keeps working exactly as WordPress does with no persistent object cache at all: slightly more database queries, but no errors and no downtime. The status panel will show the backend as Memory (not connected) and report the reason, so you can see at a glance that Redis needs attention. When Redis comes back, later requests reconnect automatically.
WP-CLI commands
If you manage your site from the command line, Speed of Light registers object-cache subcommands under wp sol object-cache. These are handy for automation, deploys, and troubleshooting on headless servers.
# Show status: drop-in state, connection, hit ratio, Redis version/memory wp sol object-cache status # Install / enable the Redis drop-in wp sol object-cache enable # Remove the drop-in (falls back to WordPress default cache) wp sol object-cache disable # Refresh the drop-in to the current plugin version wp sol object-cache update-dropin # Flush only our cached keys (never other apps' keys) wp sol object-cache flush # Low-level Redis / PHP diagnostics as a table wp sol object-cache diagnostics # List global and non-persistent cache groups wp sol object-cache groups
Standard WordPress CLI cache commands (for example wp cache flush) also work while the drop-in is active, since it is a fully compatible WordPress object cache.
The diagnostics command lists the codecs available, the serializer in use, the max TTL, the key prefix, the maxmemory policy, evicted and rejected connection counts, and more — the same figures shown in the admin panel, formatted for a terminal.
Removing object caching
Turning object caching off (in the UI or with wp sol object-cache disable) flushes our keys from Redis while the drop-in is still live, removes the object-cache.php drop-in and the SOL_REDIS_* block from wp-config.php, and leaves WordPress on its normal default cache. It never removes a foreign drop-in and never touches any WP_REDIS_* constants your host may have set. Your page cache and every other Speed of Light feature keep working unchanged.
Preloader
Caching only helps the visitors who arrive after a page has already been cached. The very first visitor to a fresh or newly-purged page normally pays the full cost of generating it — and on a busy site that "first visitor" penalty happens over and over as pages are edited and the cache is cleared. The Preloader removes that penalty. It crawls your site and warms the cache in the background so the page file is already written to disk before a real person ever asks for it. The result: your visitors get a cache HIT from the very first request, not a slow miss.
In plain terms: the Preloader visits your own pages for you, ahead of time, so your actual visitors never have to wait for a page to be built.
What the Preloader does
When a preload runs, Speed of Light builds a list of every public URL worth caching — your home page (when enabled), all published Pages, and all published posts across your public post types — then requests each one exactly the way a modern browser would. For each URL it confirms that a readable cache file was actually written to disk before counting the page as done. A page that merely loads (returns HTTP 200) is not enough; the Preloader verifies the cache file exists, and if the response came back but no file was written, it will write the cache file directly from the response body. This means the "cached" count you see is honest — it reflects pages that will genuinely serve a HIT, not pages that just happened to respond.
Preloading respects your exclusions completely. Any page, post type, URL path, or query string you have excluded from caching is skipped by the crawler too — the same exclusion rules are enforced everywhere, so an excluded page is never crawled, never cached, and never optimized.
Automatic warming after every purge
You rarely need to start a preload by hand. Whenever the cache for a page is cleared — because you published an edit, or a scheduled purge ran — Speed of Light re-queues the affected URLs and warms them again in the background. The goal is simple: the cache is refilled before your next visitor arrives, so they land on a HIT instead of triggering a fresh rebuild.
Warms the exact variant your visitors read (WebP & mobile)
Caches can hold more than one version of the same page. When automatic WebP image conversion is turned on, the cache filename for a page gains a separate variant for browsers that accept modern image formats. Because every modern browser advertises WebP/AVIF support, that is the variant your real visitors actually read. The Preloader knows this: it sends the same Accept: image/webp, image/avif signal a real browser sends, so it warms the WebP variant your visitors will hit — not an unused plain slot that nobody reads. This keeps the preloaded cache and live traffic perfectly aligned, so the first visitor gets an already-optimized page.
Only anonymous (logged-out) page views are cached, so that is what the Preloader warms. Logged-in users and any request carrying a session cookie are bypassed — they are never cached and never preloaded, by design.
Zero-config background advancement (no cron required)
A common frustration with other cache plugins is that preloading stalls the moment you close the dashboard, or never runs at all because the host has disabled WP-Cron and offers no real server cron. Speed of Light is built so this just works out of the box. As of the 1.3.x line, a preload keeps advancing on its own even when nobody is watching and no cron is configured, because it has three independent ways to keep moving:
- Non-blocking background loopback. After each batch, the Preloader fires a tiny, fire-and-forget internal request that immediately starts the next batch. It doesn't wait for a browser tab, WP-Cron, or a server cron job — the crawl advances itself.
- WP-Cron fallback. On hosts that block internal loopback requests, a scheduled event picks up the next batch instead, so progress continues.
- Stall watchdog. If a host somehow blocks both of the above at once, a self-healing watchdog notices that progress hasn't advanced and restarts the crawl the next time any admin page is loaded. It is throttled so it can never double-run or accidentally start a crawl.
You do not need to set up WP-Cron or a server cron job for preloading to finish. Start it once and it runs itself to completion in the background.
Adaptive throughput
The Preloader tunes itself to your server instead of hammering it with a fixed rate. It sizes each batch, decides how many pages to request at once, and paces itself based on how fast your site is actually responding. It measures its real throughput as it goes (smoothing the number so a single slow page doesn't throw off the estimate) and uses that live measurement to show you a realistic "time remaining" and an estimated time-per-1,000-URLs. If a page fails, it is retried automatically a few times before being recorded as failed, so a transient hiccup doesn't leave a gap in your cache. Redirects are followed and the final destination is warmed instead.
Turbo mode
For a fast rebuild — for example right after a big content import, or on a well-resourced server — Turbo mode raises the concurrency and batch sizes so the whole site warms much faster. Use standard mode on shared or modest hosting where you'd rather keep the crawl gentle; use Turbo when you want the cache filled as quickly as your server can handle it.
When a CDN or firewall blocks the crawl
On some hosts a CDN, a web application firewall (WAF), or a security plugin will return a 403 Forbidden to an automated request — even though a normal browser loads the page fine. If that happens, preloaded pages can't be warmed, and other tools would just report a meaningless "403" and leave you guessing which layer is at fault.
Speed of Light does better: when a page is blocked, the diagnostic names the actual blocker. It inspects the failing response and tells you, in plain language, exactly what turned the crawler away — for example:
- Cloudflare / CDN edge (it even captures the Cloudflare Ray ID so your host can trace it)
- ModSecurity (origin WAF)
- Wordfence
- origin (nginx), origin (Apache), or origin (LiteSpeed)
This turns a dead-end error into an actionable next step: you know whether to allow the crawler through your CDN, relax a firewall rule, or whitelist the request at your origin. The failure entry also records the HTTP status, the returning server, and a short snippet of the block page so you (or your host's support) can pinpoint the rule.
If your preload reports 403s named Cloudflare or a WAF, the fix is to allow Speed of Light's crawl to reach your site — typically by permitting the plugin's requests through the blocking layer. Once the crawler can reach the page, warming resumes automatically on the next run.
Advanced: sending the crawl straight to your origin
Developers who need the crawl to bypass a CDN or edge entirely — for hosts where the edge returns a 403 to server-side requests — can rewrite the request target with the sol_preload_request_url filter to point each preload request at the origin server directly, while the cache is still keyed to the public URL. Related filters (sol_preload_request_args, sol_preload_user_agent, sol_preload_sslverify) let you adjust headers, the User-Agent, or certificate verification for stubborn setups. These are optional escape hatches; the default behavior handles the vast majority of sites without any configuration.
Running a preload from the dashboard
Make sure Local Cache is on
Preloading warms the on-disk page cache, so Local Cache must be enabled. If it's off, the Preloader will tell you to enable it first. Speed of Light also checks that its own cache drop-in is installed and that no conflicting cache plugin has taken it over — if another caching plugin owns the drop-in, disable that plugin first.
Start the preload
Click Preload to begin. Speed of Light builds the URL list and starts warming pages immediately in the background. You can leave the page — it keeps running on its own.
Watch progress
The progress bar shows cached vs. pending URLs, the current rate, an estimated time remaining, and any failures (with the named blocker for each). You can turn on Turbo for a faster crawl, or Stop at any time to clear the queue and halt.
Preloading from WP-CLI
If you manage your site over SSH, the entire Preloader is available from the command line under wp sol preload. This is ideal for scripting a full warm after a deploy, or for kicking off very large crawls without keeping a browser open. Running the command with no flags simply queues every eligible URL and lets the background workers warm them:
# Queue every public URL and warm it in the background wp sol preload
Add --turbo for the high-concurrency crawl:
# High-concurrency warm (best on well-resourced servers) wp sol preload --turbo
Use --run to process the whole queue synchronously in the current process — the command blocks and reports a live result when it finishes, which is exactly what you want in a deploy script or CI pipeline. You can cap the total time with --max-seconds and pause between batch windows with --sleep:
# Warm the entire site right now and wait for it to finish wp sol preload --run # Run until done, but never longer than 5 minutes wp sol preload --run --max-seconds=300 # Run to completion, pausing 2s between batch windows to stay gentle wp sol preload --run --sleep=2
You can fine-tune throughput for a single run with the sizing flags. These override the defaults just for this crawl:
--batch=<number>— URLs per batch--concurrency=<number>— how many requests are sent in parallel--chunk=<number>— URLs processed inside one runtime window--runtime=<seconds>— how long each worker window runs before yielding--delay=<seconds>— pause between scheduled batches
# A big, aggressive one-shot warm with custom sizing wp sol preload --run --turbo --batch=500 --concurrency=50 --delay=1
Related commands: wp sol clear purges the cache (optionally for specific --ids, --urls, or --sites). Because clearing a page re-triggers automatic background warming, you can safely clear and let the Preloader refill without a second command.
For WP-CLI preloads to warm every page reliably, run wp from the correct WordPress path with the right permissions. If your site sits behind a CDN, make sure the crawl can reach your origin (enable origin/loopback access at your host), and if Redis blocks CLI you can keep the object cache disabled for CLI with WP_REDIS_DISABLED=1.
Reading the results
After a run, each URL is one of three things: cached (a fresh cache file exists and will serve a HIT), expired (a cache file exists but has aged past your expiry window and will be refreshed on next warm), or failed (it couldn't be cached after retries). Every failure keeps its diagnostic detail — the HTTP status, the named blocker, the server that answered, and a snippet of the response — so you're never left guessing why a page didn't warm. In almost every case, a failed page falls into one of two buckets: it's genuinely excluded from caching (expected), or a CDN/WAF is blocking the crawl (fixable by allowing the crawler through, as described above).
Bottom line: turn on Local Cache, click Preload once, and Speed of Light keeps your pages warm in the background — correct variant, no cron setup, and a clear diagnosis whenever a CDN or firewall gets in the way.
If you hit a blocker the diagnostic can't resolve for you, contact support at [email protected] with the failure detail shown for the page and we'll help you get the crawl through.
Cloudflare edge
The Cloudflare edge section connects Speed of Light to your Cloudflare account so that your full, ready-to-serve HTML pages are cached at Cloudflare's global network — the "edge" — instead of only on your own server. When a visitor in another country loads a cached page, Cloudflare answers from the data centre nearest to them, so the page arrives in milliseconds without ever touching your origin server.
This is done entirely through Cloudflare's modern Cache Rules API. Speed of Light creates and manages the rules for you automatically, so you never have to touch the Cloudflare dashboard rule editor.
It works on the free Cloudflare plan and uses zero Page Rules — your three free legacy Page Rules stay untouched and available for other uses.
What this integration is (and is not)
Speed of Light's edge caching is Cloudflare-only. It talks to Cloudflare's Cache Rules API and nothing else. To keep behaviour predictable and free-plan-compatible, it deliberately does not use:
- Cloudflare APO (Automatic Platform Optimization), Argo Smart Routing, or Tiered Cache
- Cache tags or tag-based purging (targeted purges use full URLs instead)
- Any other CDN — there is no support for a non-Cloudflare edge
What it caches is anonymous, public HTML only. Logged-in users, shoppers with a cart, commenters, and password-protected pages are never edge-cached — those requests are simply passed straight through to your server as dynamic (uncached) responses. There is no per-user or personalized caching; the cache is a single shared copy for anonymous visitors.
How the [SOL_MANAGED] rules work
When you deploy, Speed of Light writes a Cache Rule into your zone's cache-settings ruleset with the description Speed of Light: Edge Manager (Full Page HTML) [SOL_MANAGED]. The [SOL_MANAGED] marker is how the plugin recognises its own rules.
- On every deploy, the plugin removes its previous full-page HTML rule and writes a fresh one — so redeploying never stacks up duplicates.
- Any rules you or other tools created are left completely alone. The plugin only ever touches rules carrying its own marker.
- When you turn Edge Cache off, the plugin removes its full-page HTML rule and purges the edge once, so nothing keeps serving from a stale cache.
There is a separate, optional static-asset rule (Asset Manager (versioned CSS/JS) [SOL_MANAGED]) that edge-caches your versioned CSS and JS for one year. It has its own deploy/remove control and is independent of the HTML rule — turning HTML edge cache off never removes it, and redeploying HTML never removes it.
Connecting Cloudflare
Open the Credentials tab of the Edge Cache screen. You need two things: a way to authenticate, and your Zone ID (found on your domain's Overview page in Cloudflare, bottom-right).
You can authenticate in one of two ways:
API Token (recommended)
Create a scoped API Token in Cloudflare with Zone → Cache Purge and Zone → Zone → Read permissions, then paste it into the API Token field. This is the most secure option because the token is limited to just the permissions the plugin needs.
Global API Key (legacy fallback)
Alternatively, enter your Cloudflare account Email plus your Global API Key. This works but is less safe — the Global Key can do anything on your account — so prefer the API Token for production sites.
Enter your Zone ID
Paste the Zone ID for the domain you want to accelerate. The plugin treats Cloudflare as "configured" only once it has a Zone ID and either a token or an email + Global Key pair.
Deploy the cache rules
Save, then deploy. The plugin creates the [SOL_MANAGED] full-page HTML rule in your zone and purges the edge once so caching starts clean.
Your credentials stay on your server
Your API Token, Global API Key, and email are stored on your own WordPress site and used only server-to-server when Speed of Light calls Cloudflare. They are treated as write-only secrets.
- Never sent back to the browser. When the dashboard loads your edge settings, the secret fields are blanked out and replaced with a simple "is set / not set" flag. The actual token or key is never returned to the browser after you save it.
- Never included in exports. When you export your settings to a JSON file, the Cloudflare API token, API key, and legacy token alias are stripped out first. You can safely share or reuse an export without leaking credentials — you just re-enter them on the new site.
Edge Cache TTL and exclusions
On the Cache Config tab you control how long pages live at the edge and which URLs must never be edge-cached.
Edge Cache TTL is a dropdown: 1 Hour, 2 Hours, 4 Hours, 1 Day, 7 Days, or 30 Days. This is how long Cloudflare keeps a cached page before revalidating it. You rarely need a short TTL, because Speed of Light purges the edge automatically whenever content changes (see below). The HTML rule also serves-stale-while-revalidating, so any revalidation after the TTL is invisible to visitors — they never wait on a blank page.
Custom Exclusions lets you add extra edge-only paths (one per line, e.g. /checkout*). You generally do not need to duplicate anything here — see the next section on what is bypassed automatically.
Any URLs, query strings, or cookies you exclude in the plugin's main Cache settings (Local Cache exclusions) are automatically synced into the Cloudflare rule too. If you change a local exclusion, the edge rule redeploys itself so the two never drift apart. Use the edge-only Custom Exclusions box only for paths you want excluded at the edge but not locally.
Dynamic bypass at the edge
Speed of Light builds the Cloudflare rule so that only truly anonymous, public pages are cached. Everything below is passed through to your server as a live, uncached response — automatically, without you configuring anything:
- Logged-in users — any request carrying a
wordpress_logged_in/wordpress_secsession cookie - WooCommerce and Easy Digital Downloads carts / sessions —
woocommerce_*,wp_woocommerce_session_*,edd_*, plus other cart/session prefixes (Ecwid, WP Simple Cart, YITH, Bookly and more) - Password-protected posts (
wp-postpass) and commenters (comment_*) - Admin, login, REST API and XML-RPC paths —
/wp-admin,/wp-login,/wp-json,/xmlrpc.php - Cart, checkout and account pages —
/cart,/checkout,/my-account - Add-to-cart, AJAX and preview requests — markers like
add-to-cart=,wc-ajax=,preview=true - Static asset directories —
/wp-content/and/wp-includes/are never treated as full-page HTML
Because the edge rule sends these straight to origin, there is no way for a shopper's cart or a logged-in dashboard to accidentally leak into the shared public cache.
Custom cookie bypass
If you run a membership or personalization plugin that uses its own cookie, add that cookie name (one per line) to the Custom Cookie Bypass box on the Cache Config tab. Any request carrying that cookie will then skip the edge cache. Cookie exclusions from your Local Cache settings are folded in here automatically as well.
The plugin keeps the generated rule under Cloudflare's hard 4096-character expression limit. It de-duplicates overlapping cookie and path patterns and, if a heavily-excluded site would still exceed the limit, it sheds the lowest-value exclusions first (feeds, sitemaps, robots) and logs exactly what it dropped. The critical bypasses — logged-in users, carts, password-protected pages, admin and checkout — are in the never-dropped core and are always kept.
103 Early Hints
Speed of Light can send 103 Early Hints — a preview response the browser receives before the full HTML, telling it to start downloading key assets early. On Cloudflare (free plan included), this shaves time off page loads because the browser preconnects and preloads while your page is still being generated.
Enable 103 Early Hints in the plugin (it is a Safe-rated toggle in the CSS/Critical CSS area) and enable Early Hints in your Cloudflare dashboard. Speed of Light then emits Link: headers with rel=preconnect for your configured preconnect origins and rel=preload for manual LCP images — but only on safe, cacheable, HTTP 200 page requests. It never sends hints to logged-in users, on admin/API/feed requests, or on error pages.
Customising the hints (developers)
The exact list of Link headers passes through the sol_early_hints_links filter before it is sent, so you can add, remove, or reorder hints in a small snippet:
// Add an extra preload hint to the 103 Early Hints response. add_filter( 'sol_early_hints_links', function( $links, $settings ) { $links[] = '<https://example.com/fonts/brand.woff2>; rel=preload; as=font; crossorigin'; return $links; }, 10, 2 );
Each entry is a standard HTTP Link header value. Return the array unchanged to leave the default hints in place, or return an empty array to suppress Early Hints for a given request.
Smart purge — the edge stays fresh automatically
You should almost never have to purge Cloudflare by hand. Speed of Light watches for the events that change what a page looks like and purges the affected URLs at the edge the moment they happen:
- Publishing or updating a post/page — purges that URL plus its related archives (home, category/tag/term pages, author, feed, shop pages). This is a targeted per-URL purge, not a full wipe.
- Unpublishing, trashing, or deleting — the moment a page leaves the public "publish" status, its live URL is evicted so a hidden or removed page never keeps serving from the edge.
- An approved comment — purges the commented-on post.
- WooCommerce stock changes — an order reducing stock, or a manual stock update, triggers a purge.
- Creating, editing, or deleting a term (category, tag, custom taxonomy) — purges that term's archive and the home page.
- Switching themes, saving the Customizer, editing menus or widgets, and changes to key site settings (site title, permalinks, front-page, comment options, site icon, and similar) — trigger a full edge purge, since these can affect every page.
Targeted purges cover up to the 30 most-relevant URLs per event to stay within Cloudflare's request limits. Broad changes (theme, menus, settings) use a full purge everything. Both the successes and any failures of these edge purges are written to the plugin's log so you can confirm they ran.
Manual purge
You can always clear the edge yourself:
- Purge button in the Edge Cache screen — clears the entire Cloudflare cache for the zone (purge everything).
- Admin bar — the Speed of Light menu in the WordPress toolbar includes a purge action.
- Importing settings or running a reset also triggers an edge purge, so the edge can never be left holding pages built under old settings.
WP-CLI
On the command line, clearing the Speed of Light cache also clears the Cloudflare edge:
# Clear the whole site cache (local + Cloudflare edge) wp sol clear
The command reports success and, on multisite, can target specific sites. Clearing the cache this way fires the same edge purge as the dashboard button.
Edge caching only takes effect once Cloudflare is fully connected (Zone ID + token or Global Key) and you have deployed the cache rules. If the master Edge Cache toggle is off, or the plugin can't reach Cloudflare, pages simply fall back to your normal local caching and origin serving — nothing breaks, you just lose the edge layer until it's reconnected.
CSS optimization
CSS is one of the most common causes of a slow first paint. Every stylesheet a page links to is render-blocking by default: the browser will not draw a single pixel until it has downloaded and parsed all of them. Speed of Light gives you four complementary tools to fix this — Minify, Combine, Remove Unused CSS, and Critical CSS — plus automatic font-display: swap injection so web fonts never leave your text invisible.
Everything in this section is off by default. Speed of Light ships safe: no CSS is minified, combined, or stripped until you turn a feature on (or a licensed cloud plan enables Used CSS for you — see below). This lets you add the site to cache first, confirm it looks right, then layer optimizations one at a time.
How CSS delivery works, in plain terms
When a visitor opens a page, their browser has to fetch every <link rel="stylesheet"> before it can show anything. The more stylesheets, and the larger they are, the longer the visitor stares at a blank screen. The features below attack this from two angles:
- Make the CSS smaller — Minify strips comments and whitespace; Remove Unused CSS deletes rules the page never uses.
- Make the CSS non-blocking — Critical CSS inlines just the styles needed for the top of the page and defers the rest so the browser paints immediately.
CSS Minify
Minify rewrites each of your local stylesheets into a compact version with comments, indentation, and redundant whitespace removed. The result is a byte-for-byte smaller file that behaves identically, cached under wp-content/cache/ and served in place of the original.
The minifier is deliberately careful about two things that naive minifiers get wrong:
- It preserves the meaningful space in descendant selectors that use modern
:is()/:where()pseudo-classes (which WordPress 6.6+ emits), so.parent :hoveris never silently collapsed into.parent:hover— which would change what the rule targets. - It never touches the
+operator insidecalc(), and it keeps the>=/<=range syntax in modern media queries intact.
Relative asset paths inside your CSS (background images, icon fonts, SVG masks) are automatically rewritten to absolute paths, so nothing 404s once the file is served from the cache directory.
Small-file inlining
As a bonus, when Minify is on, very small minified stylesheets (up to 4 KB each, and up to 16 KB of them per page) are inlined directly into the HTML as a <style> block instead of being served as a separate file. This removes an extra network round-trip for tiny sheets. This behavior — and its size limits — can be tuned or disabled by developers via the sol_css_inline_small_minified_enabled, sol_css_inline_small_file_threshold, and sol_css_inline_small_page_budget filters.
CSS Combine
Combine merges your local stylesheets into a single bundle, which is then minified. Fewer files means fewer requests. Speed of Light applies this intelligently:
- If the combined bundle is small (30 KB or under), it is inlined straight into the page as one
<style>block — zero extra requests. - If it is larger, it is written to a single cached file and loaded render-blocking with
media="all". Combine deliberately does not defer the whole bundle, because deferring all CSS with no above-the-fold fallback would cause a flash of unstyled content. Deferring is the job of Critical CSS instead.
The combined file's cache key includes each source file's modification time, so editing a stylesheet — or updating the theme or plugin that ships it — automatically rebuilds the bundle. Stylesheets are kept in their original cascade position relative to inline <style> blocks, so combining never inverts your builder's per-widget style precedence.
What Combine skips (safely). Stylesheets scoped to a non-standard media query (e.g. print), sheets that contain an @import (which must live at the top of a file), off-site stylesheets, and any sheet you list in the combine exclude box are all left out of the bundle. Skipped sheets are still minified in place, so you lose nothing.
Combine and Critical CSS are mutually exclusive. If Combine is active, Critical CSS turns itself off for that request — combining folds everything into one sheet, which is the opposite of Critical CSS's split-and-defer strategy. Use one or the other, not both.
Excluding specific stylesheets
Both Combine and Minify have their own exclude lists in the settings. Enter one substring per line; any stylesheet whose URL contains that text is left untouched. This is the escape hatch for a stylesheet that a particular theme or plugin needs served exactly as-is.
Remove Unused CSS (Used CSS per URL)
Remove Unused CSS — "RUCSS" — is the flagship CSS win. For each URL, Speed of Light reads every stylesheet the page loads, compares each rule's selectors against the actual HTML on that page, and writes a single lean used-CSS file containing only the rules that page actually needs. The original render-blocking stylesheets are then replaced by that one small file. Because it works per-URL, your homepage, a blog post, and a product page each get their own precisely-trimmed CSS.
Why it is safe by design
The biggest fear with any unused-CSS tool is that it strips a rule the page needs — for example a class that JavaScript adds after the page loads (a dropdown that opens, a slider that becomes active). Speed of Light guards against this on several layers:
Automatic safelist
A large built-in safelist of class prefixes is never removed. It covers WordPress core and Gutenberg blocks, the admin bar, WooCommerce, GreenShift, Elementor, Divi, Beaver Builder, Bricks, Breakdance, Oxygen, GenerateBlocks, Spectra, Stackable, Kadence Blocks, block themes (FSE), common sliders and lightboxes (Swiper, Slick, Owl, Fancybox…), animation libraries (AOS, WOW, Animate.css), popular form plugins (Contact Form 7, WPForms, Gravity Forms), cookie/GDPR banners, multilingual plugins, and generic interaction states like active, open, is-visible, and hover.
Builder-aware auto-detection
On top of that fixed list, Speed of Light detects which page builders, block themes, and plugins are actually active on your site and merges in their specific class patterns and any stylesheets that are too dynamic to trim. This is what lets it "just work" across 20+ builders without configuration.
Conservative selector matching
Any rule using a pseudo-class or pseudo-element (:hover, :focus, :nth-child, ::before, :is(), :has()…), a universal selector (*), or an attribute selector ([data-…]) is always kept. When in doubt, the rule stays.
Deferred-original safety net
By default, the original stylesheets are not deleted — they are re-added after the lean used-CSS as non-blocking deferred loaders (with a <noscript> fallback for visitors with JavaScript off). The visitor paints instantly from the small used-CSS, and moments later the full originals load and cascade in, restoring anything the matcher may have missed. You get the speed with essentially none of the risk.
Inline styles are never stripped. Speed of Light only ever removes external <link> stylesheets. Inline <style> blocks — including the per-block layout, spacing, and duotone CSS that Gutenberg and block themes print into the page — are always left exactly where they are. Their rules are still folded into the used-CSS, but the original block is never removed, so block-theme layouts can never collapse.
Coverage sanity check
If a large stylesheet ever collapses to an implausibly tiny used-CSS file, Speed of Light assumes the page had JavaScript-rendered markup its matcher couldn't see, and serves the original untouched CSS for that request rather than risk an unstyled page. Generation is also bounded by a hard time budget, so a CSS-heavy page can never stall the visitor's request — if generation runs long, the original CSS is served instead.
@import and web fonts
When Speed of Light builds a used-CSS file, it resolves @import statements by pulling the imported stylesheet's rules directly into the file, so the lean file never triggers extra render-blocking third-party fetches. Any web fonts imported this way (for example from Google Fonts) are self-hosted automatically, so the visitor never opens a connection to a third-party font server. If any import can't be resolved safely, the original statement is simply kept — the site never loses CSS to this step.
Disabling Used CSS for one page
Some pages are special — a heavily interactive landing page, a page builder preview, a page with complex JavaScript-driven styling. For those, edit the page and use the Speed of Light meta box to disable Remove Unused CSS for that single post. Every other page keeps its optimized CSS.
Extending the safelist in code (for developers)
You can add your own class prefixes to the merged safelist with the sol_rucss_auto_safelist filter. Any selector containing one of your prefixes will always be preserved:
// Never strip rules for these custom classes. add_filter( 'sol_rucss_auto_safelist', function( $safelist ) { $safelist[] = 'my-plugin-'; // prefix match $safelist[] = 'js-animated'; // added by your JS return $safelist; } );
Non-developers never need this — the automatic and builder-aware safelists already cover the common cases. There is also a plain-text safelist box in the settings for the same purpose without writing code.
Critical CSS
Critical CSS takes a different approach to the same problem. Instead of trimming your stylesheets, it extracts just the CSS needed to paint the top of the page ("above the fold") and inlines that directly into the HTML. The rest of the CSS is then deferred — loaded in a way that does not block the first paint — so the visitor sees a fully-styled hero section almost instantly, and the remaining styles stream in right behind it.
Critical CSS is generated per URL and cached, so each page type gets its own above-the-fold styles. The cached file is keyed by a canonical form of the URL (query strings and fragments dropped, www and trailing-slash normalized), so clearing one page's cache re-generates exactly that page and nothing else.
FOUC-safe by design
A "flash of unstyled content" (FOUC) happens when a page briefly appears unstyled while CSS loads. Speed of Light avoids it carefully:
- On a visitor's first visit to an uncached page, it generates and stores the critical CSS but does not defer the stylesheets on that same request — the visitor still gets the full CSS normally. Only once valid critical CSS is confirmed and cached does the deferred, optimized path kick in for subsequent visits.
- It only defers stylesheets when it has a substantial amount of critical CSS to inline (more than 500 bytes). If the extracted critical CSS is too thin to be trusted, stylesheets keep loading normally.
- Deferred stylesheets always carry a
<noscript>fallback, so visitors with JavaScript disabled get the full CSS.
Theme-aware auto-skip. Some modern themes and builders — Blocksy, Kadence, GeneratePress, Astra, Neve, Flatsome, Avada, Divi, and builders like GreenShift, Oxygen, and Bricks — already generate their own above-the-fold critical CSS. Deferring their stylesheets would only fight the theme and push your hero paint later. Speed of Light detects these and automatically declines to defer, which is always the safe choice (your full CSS keeps loading normally, never a FOUC). Developers can override this with the sol_critical_css_theme_skip filter.
Font handling and the deferral method
Any @font-face rule that lands in the critical CSS automatically gets font-display: swap added if it doesn't already declare one, so a slow font never leaves your heading text invisible (a "flash of invisible text"). Deferred stylesheets are loaded with the well-tested media="print" / onload swap technique by default; an alternative data-href deferral method — which withholds the sheet from the browser entirely until a self-contained loader swaps it in — is available for themes where the default method behaves oddly.
Automatic font-display: swap
Independently of which CSS features you enable, whenever Speed of Light inlines critical CSS it injects font-display: swap into any bare @font-face rule. This tells the browser to show your text immediately in a fallback font and swap to the web font once it arrives, rather than hiding the text while the font downloads. It is idempotent — faces that already declare a font-display value are left untouched — and it directly improves your Largest Contentful Paint.
Cloud-assisted generation (licensed sites)
Speed of Light can offload both Used CSS and Critical CSS generation to the Speed of Light cloud, where a real headless Chrome renders your page and measures exactly which CSS it uses and which rules paint above the fold. This is far more accurate than parsing HTML on your own server, because it sees the page the way a real browser does — including layout the server-side matcher can only approximate.
Key points about how this works:
- One render, both outputs. A single cloud analysis produces both the used-CSS and the critical-CSS slice for a URL, so there is no duplicate work.
- It only runs on a cache miss. On a well-tuned site, that means the cloud is called by the preloader as it warms pages — so real visitors are almost always served the warm page cache with the cloud result already baked in, never waiting on a network call.
- Local fallback, always. If the cloud is unavailable, unlicensed, or returns no usable result, generation falls straight through to the on-server builder described above. You never lose the optimization; you just fall back to the local method.
- Circuit breaker. If the cloud API is down or slow, a circuit breaker stops Speed of Light from repeatedly calling it, so a cloud outage can never add latency to your pages.
- Auto-enable for Used CSS. Because the cloud measurement is so accurate, a licensed site with cloud acceleration on will use cloud-generated Used CSS even if the local Used CSS toggle is off — unless you've specifically chosen Critical CSS instead (the two remain mutually exclusive). The deferred-original safety net still applies.
Logged-in users are always protected. None of these CSS optimizations run while the WordPress admin bar is showing. For logged-in users, stylesheets — including the admin bar's own CSS and the Speed of Light toolbar — are served in full and untouched, so your editing experience never breaks. Optimizations apply only to the anonymous, logged-out pages your visitors see. When our own cloud renderer fetches a page to analyze it, it is likewise served the full, un-optimized CSS so it measures the real layout.
Choosing your setup
You do not need to enable everything. A good, low-risk progression:
- Turn on Minify first — it is the safest win and rarely causes any visual change.
- Add Remove Unused CSS (or let cloud acceleration drive it) for the biggest reduction in render-blocking CSS. Keep the deferred-original safety net on while you verify pages look right.
- If you prefer the inline-critical-CSS approach over Used CSS, use Critical CSS instead — remember they are mutually exclusive, and Critical CSS is also mutually exclusive with Combine.
- Use Combine when you have many small local stylesheets and want to cut request count, on sites where Critical CSS isn't in play.
After enabling any CSS feature, clear the cache, load a few key pages as a logged-out visitor (or in a private window), and confirm they render correctly. If a specific page misbehaves, disable Used CSS for just that page via its meta box, or add the relevant class prefix to the safelist — you rarely need to turn a whole feature off.
If you get stuck, reach out at [email protected] with the URL of the affected page and which CSS features you have enabled.
JavaScript optimization
JavaScript is the single biggest drag on how a page feels. Even after the HTML is painted, every third-party tag — analytics, chat widgets, social embeds, sliders — keeps the browser's main thread busy parsing and executing script, which is exactly what a visitor's first tap or click has to wait for. Speed of Light attacks this in two independent ways: it delays non-essential scripts until the visitor actually interacts with the page, and it minifies your local JavaScript files. Neither combines or concatenates files — there is deliberately no bundling step — so nothing about your site's script loading order is silently rewritten.
Both features are safe to leave on for the vast majority of sites. This page explains exactly what stays untouched, why, and the handful of controls you have if you ever need to make an exception.
Delay JavaScript until interaction
When "Delay JavaScript" is enabled, Speed of Light rewrites eligible script tags so they do not run during the initial page load. Instead, the plugin injects a tiny inline runner high in the <head> that watches for the first sign of a real visitor: a click, tap, scroll, key press, or mouse movement. The moment any of that happens, the delayed scripts are restored and executed in their original order. If no interaction ever occurs, a timeout fires and loads them anyway so nothing is left permanently unloaded.
The INP and responsiveness win
Because the browser's main thread is no longer tied up executing third-party scripts while the page is painting, the page becomes interactive far sooner. This directly improves the Core Web Vitals metrics that punish script-heavy pages the hardest — INP (Interaction to Next Paint) and Total Blocking Time — and usually lifts the reported LCP as well, since heavy scripts are no longer competing with your hero content for CPU. On typical sites loaded down with tag managers, chat widgets, and analytics, this is the largest single scoring improvement the plugin can make.
Timeout fallback
The Interaction Timeout setting is your safety net. If a visitor never interacts — for example a bot, a screen reader that scrolls programmatically, or someone who reads a page and leaves — the delayed scripts still load automatically once the timeout elapses. The default is 3500 ms. You can set any value from 0 to 30000 ms. A value of 0 means "wait forever for interaction" and never fires the fallback timer — use it only if you are certain every meaningful path involves interaction.
Delay modes
Delay behavior is controlled by a single per-feature Delay Mode — there is no separate global "developer" or "one-click" mode elsewhere in the plugin that changes this. The three modes are:
- Safe (recommended) — Delays non-critical scripts but automatically excludes forms, sliders, checkout, cookie banners, and other interactive plugin scripts. This is the right choice for almost every site.
- Aggressive — Delays essentially everything except the built-in critical set (payments, ads, analytics, CAPTCHAs — see below). Squeezes out the most performance, but test interactive elements afterward.
- Custom — Ignores the automatic plugin-critical safety list and delays based on your exclusions list only. For advanced users who want full manual control.
In every mode, a script that already carries async or defer is left completely alone. Those scripts are already non-blocking, and re-processing them would risk breaking the execution order that builder runtimes (Elementor, Divi, Bricks) and their sliders, tabs, and accordions depend on.
Ad-safe and revenue-safe by default
Delaying the wrong script can break tracking, hide ads, or stop a purchase — so Speed of Light ships with a built-in "never delay" list that applies in every mode, including Aggressive. These scripts are always loaded eagerly:
- Advertising — Google AdSense (
adsbygoogle,pagead2.googlesyndication.com), Google Publisher Tag / GPT (gpt.js,googletag,pubads), DoubleClick (googleads.g.doubleclick.net,securepubads.g.doubleclick.net), Google Ad services, and Funding Choices. Delaying an ad loader means ads never render for visitors who don't interact — which directly kills ad revenue — so these stay eager. - Analytics & tag managers — Google Tag Manager (
gtm.js), GA4 / gtag (gtag,google-analytics,analytics.js), the data layer, and Facebook Pixel (connect.facebook.net,fbevents.js). Measurement stays intact. - CAPTCHAs & challenges — Cloudflare Turnstile, Google reCAPTCHA, and hCaptcha. Delaying these would break form submissions and login challenges.
- Payments & commerce — Stripe, PayPal, Square, Braintree, and WooCommerce's core cart/checkout scripts.
- Core WordPress & jQuery — jQuery and jQuery Migrate, plus core dependencies like
wp-hooks,wp-i18n,wp-util, Underscore, Backbone, comment-reply, and the admin bar's own scripts.
WooCommerce and logged-in users are never touched
Beyond the script-level list, delay is skipped entirely for whole categories of requests:
- WooCommerce transactional pages — the cart, checkout, and My Account pages never have their JavaScript delayed. These flows depend on script running immediately (payment fields, AJAX cart updates, address validation), so delaying them is disabled outright to protect purchases.
- Logged-in users and the admin bar — when the WordPress admin bar is showing, delay is skipped so the toolbar's own scripts always work. There is also a "Skip for logged-in users" toggle (on by default) so administrators and editors always see the un-delayed page, keeping admin interfaces from breaking.
- Non-page requests — the engine never runs in wp-admin, AJAX, cron, the REST API, or the Customizer.
Scripts that can't be safely delayed
Some scripts are skipped automatically for technical safety, regardless of mode:
- ES modules (
type="module") — these are already deferred by the browser, and re-injecting them would break import resolution and ordering. - Inline scripts containing
document.write()or a literal</script>— these cannot be safely extracted and replayed after load. - Any tag you explicitly mark with
data-sol-no-delay.
Excluding a specific script from delay
If a widget misbehaves after enabling delay, you can protect it in the Exclusions box (one pattern per line) without leaving the mode. Patterns are matched against the script's src, its id, or its inline content:
- A plain string is a partial match, e.g.
google-analytics.comormy-custom-script.js. *works as a wildcard, e.g.*chat*to protect any chat widget.- An exact script handle is matched precisely (with or without WordPress's trailing
-jssuffix), so you can protect one handle without accidentally matching similarly named scripts.
Fastest way to confirm delay is (or isn't) the cause of a problem: append ?soldelay=0 to the page URL. That disables JS delay for that single page load only, leaving your site-wide setting untouched — perfect for A/B checking a broken widget.
Excluding scripts in code
Developers can extend the automatic plugin-critical safety list — the scripts that are protected in Safe mode — using the sol_js_delay_plugin_exclusions filter. Add substrings that should match a script's src, id, or inline content:
// In your theme's functions.php or a small mu-plugin. add_filter( 'sol_js_delay_plugin_exclusions', function( $patterns ) { $patterns[] = 'my-booking-widget'; // matched against src / id $patterns[] = 'live-availability'; return $patterns; } );
A companion filter, sol_js_delay_builtin_critical, lets you extend the always-eager built-in list (the ads/analytics/payments set) if you have another script that must never be delayed in any mode.
There is no per-post checkbox for JavaScript delay. To turn delay off for one specific page, use the ?soldelay=0 query string (for testing) or add a targeted line to the Exclusions box. The per-post checkbox described below applies only to JS minification.
Local JavaScript minification
Separately from delay, "Minify JavaScript" strips comments, whitespace, and other dead weight from your local JavaScript files to reduce their transfer size. It runs independently — you can use either feature alone or both together.
What gets minified — and what's skipped
- Local files only. Cross-origin scripts (anything hosted on a different domain than your site) are never fetched or rewritten.
- Already-minified files are skipped. Any URL containing
.min.jsis left alone. As a second guard, if the minified output isn't actually smaller than the original — common with pre-built bundler output likeapp.[hash].jsor vendor chunks — the plugin discards it and serves the original untouched. - ES modules are skipped (
type="module"), since minifying them can break import paths. - Data URIs and non-JavaScript types (e.g. JSON-LD blocks) are skipped.
- Any script tagged
data-sol-no-minify, or matching a line in the minify Exclusions box, is skipped. - Inline scripts are also minified when they're large enough to be worth it and actually shrink.
Content-addressed cache
Minified output is written once to a cache directory and reused. Each cached file is keyed by a hash of the original URL and its file modification time, so the moment you update a source file (a plugin update, a theme edit), the key changes and a fresh minified copy is generated automatically — you never serve stale minified code. Files are written atomically (to a temp file, then renamed) so a half-written file can never be served, and the cache is cleared automatically whenever site or complete cache is cleared. On multisite, each site gets its own subdirectory.
Disabling minification per post
If minification causes trouble on one specific page or post, you don't have to turn it off site-wide. Each post and page has a Speed of Light meta box with a "Disable JS minification" checkbox:
Open the page for editing
Edit the specific post or page in the WordPress editor.
Find the Speed of Light meta box
Locate the plugin's meta box in the editor sidebar or below the content.
Tick "Disable JS minification"
Check the box and update the post. JavaScript on that single page will now be served un-minified, while the rest of your site is unaffected.
Speed of Light does not combine or concatenate JavaScript, and it does not touch script loading order beyond delay and minify. If a page still feels script-heavy after enabling both features, the remaining weight is almost always third-party tags — which delay, not minify, is designed to handle. If you get stuck, reach out at [email protected].
Images
Images are almost always the heaviest thing a browser downloads, and unsized or eagerly-loaded images are a leading cause of poor Core Web Vitals scores. The Images section brings everything about image performance under one roof: it converts your media library to modern, lighter formats on your own server, it hands the browser the right loading hints (lazy-load, async decode, explicit dimensions), and it works out which image is your page's true hero so it can be loaded first. Everything here happens on your hosting — there is no image CDN and nothing is sent off-site for delivery.
Two things are worth separating in your head from the start. Conversion permanently rewrites the files in your media library to WebP or AVIF. Front-end hints (lazy-load, dimensions, LCP handling, facades) change only the HTML sent to visitors and never touch your files. You can use either half on its own.
Format conversion (WebP and AVIF)
Speed of Light re-encodes your JPEG, PNG, and GIF images into WebP (the default) or AVIF. These modern formats typically produce much smaller files at the same visual quality, which means faster loads and less bandwidth. Conversion runs entirely on your server using whichever image engine WordPress already has available — Imagick or GD — with an automatic fall-back to the cwebp command-line binary if the built-in engine cannot produce WebP. Images uploaded through the WordPress media library are also converted right in your browser (using the browser's own canvas encoder) before they ever reach your server, so the upload itself is already smaller.
What conversion actually does to a file
Conversion is in-place and permanent. When an image converts successfully, the plugin regenerates every thumbnail size from the new file, re-points the attachment in WordPress, and then rewrites every URL that referenced the old image — in post content, in page-builder layouts, in theme options, and in serialized metadata — so nothing is left pointing at a file that no longer exists. The old original and its old thumbnails are deleted only after the database has been safely updated.
Because conversion replaces the originals, there is no keep-a-copy or one-click restore. This is a deliberate design choice that keeps your uploads folder lean. Before running a large bulk conversion for the first time, take a normal backup of your wp-content/uploads directory and database so you always have a way back.
Quality-aware, and it never enlarges or bloats
Conversion is built to be safe by default:
- Quality is configurable (1–100, default 82). Higher keeps more detail and produces bigger files; lower saves more space. 82 is a well-balanced starting point where the difference is invisible on almost all photos.
- It never keeps a bigger file. If the converted image comes out the same size or larger than the original (which can happen with already-optimized images or simple flat graphics), the plugin discards the conversion and keeps your original untouched. You will never end up with a "-15% saved" that is actually a loss.
- It never up-scales. Conversion changes the format, not the resolution. Your images are never enlarged.
- Animated GIFs are skipped on the browser-side upload path, so animations are preserved rather than being flattened to a single frame.
Optional downscale on upload
Many sites receive enormous camera-original uploads (4000px and up) that are never displayed anywhere near that size. Set a maximum dimension to have the browser downscale the longest edge of new uploads to that limit before conversion — for example, capping everything at 1920px. The default is 0, meaning no downscale (WordPress's own sizes still apply). This is applied to new uploads; it does not retroactively shrink images you convert in bulk.
Presets
Three starting points are available so you don't have to reason about numbers:
- Custom — you set quality, format, and dimension yourself (the default).
- Recommended — quality 82, WebP output. A safe, high-quality baseline.
- Small — quality 70, WebP output, and a 1920px maximum dimension. Prioritizes the smallest files for bandwidth-sensitive sites.
WebP vs AVIF
WebP is the default because it is universally supported by modern browsers and every capable server can produce it. AVIF compresses even harder but requires your server's image engine to support AVIF encoding, so it is only offered when your host can actually produce it. If you select a format your server cannot encode, the plugin automatically falls back rather than silently failing on every image: AVIF falls back to WebP, and WebP falls back to leaving the original untouched. The Images screen reports your server's detected Imagick/GD versions and exactly which formats it can encode, so there is never any guesswork.
Conversion cannot be switched on at all if your server can encode neither WebP nor AVIF. Rather than pretend to work and fail on every image, the setting refuses to enable. If you see conversion greyed out, ask your host to enable WebP support in Imagick or GD.
Converting your existing library
New uploads are handled automatically. To convert the images you already have, you have three routes.
Bulk convert from the dashboard
Open the Images screen and start a bulk conversion. Speed of Light finds every convertible JPEG, PNG, and GIF attachment and processes them in small batches (10 at a time) so a big library never times out or overloads your server. You can watch live progress — processed, succeeded, skipped, failed, and total space saved — and stop at any time; already-converted images stay converted.
Convert on upload
Nothing to do — once conversion is enabled, every new image you add through the media library is converted in the browser before upload. Skipped images (for example, animated GIFs, or files that would get larger) are simply uploaded in their original format.
Convert via WP-CLI
For large sites or scripted deployments, drive conversion from the command line. This runs in the same batched, safe pipeline as the dashboard.
WP-CLI reference
# Convert every convertible image in the library wp sol convert # Preview what WOULD be converted, without touching any files wp sol convert --dry-run # Convert only the first 100 attachments (useful for staged rollouts) wp sol convert --limit=100 # Convert specific attachments by ID wp sol convert --ids=123,456 # Convert a single attachment and see the before/after sizes wp sol convert-one 123 # Reset saved bulk-conversion progress (start a fresh run) wp sol convert-reset
Every image that succeeds records its own savings, so the plugin can show you per-image before/after sizes and a running total of bytes saved across your whole library. Images that were already converted, or that are not a convertible type, are reported as skipped rather than failed.
Front-end delivery hints (lazy-load, decoding, dimensions)
These settings change only the HTML delivered to visitors — your files are never modified — and they are the fastest, safest wins for Core Web Vitals. Each can be toggled independently, and all three are on by default.
- Lazy-load images and iframes — adds
loading="lazy"so off-screen images and embeds are fetched only as the visitor scrolls toward them. The single hero image the plugin identifies is deliberately kept eager (see LCP below) so lazy-loading never hurts your most important image. - Async decoding — adds
decoding="async"so image decoding happens off the main thread and doesn't block the page from becoming interactive. - Add width & height — ensures images carry explicit dimensions so the browser can reserve the right space before the image arrives. This is the primary defence against Cumulative Layout Shift — the annoying jump where content leaps down as an image loads. Speed of Light adds dimensions both to images WordPress renders itself and to raw images in your header, footer, and page-builder markup that WordPress's own filters miss, by reading the real pixel size of the local file.
Lazy-load, async decoding, and dimensions benefit everyone, so they apply to logged-in users too. The one LCP-specific hint below (fetchpriority on the hero) is a visitor-facing optimization and is skipped for logged-in admins so it can't interfere with editing.
LCP: loading your hero image first
Largest Contentful Paint (LCP) is the Core Web Vitals metric that measures how quickly your page's biggest above-the-fold element paints — almost always the hero image. Getting this right is usually the difference between a green and a red Lighthouse score, and it is where Speed of Light does its most careful work.
Finding the real hero, not just the first image
Naively promoting the first <img> on the page is a classic mistake — the first image is often a tiny logo or icon. Speed of Light instead scores the candidates in the above-the-fold region by size, position, hero-like class names (hero, banner, cover, featured, masthead, and similar), and whether they sit inside your <header> or <main>, then picks the genuine hero. It understands several real-world patterns that trip up simpler tools:
- Lazy-loaded heroes. Many themes and prior lazy-load plugins hide the real image URL in a
data-srcattribute and put a 1-pixel placeholder insrc. Speed of Light detects this, promotes the real image, and removes the lazy attributes so the hero loads immediately instead of waiting for JavaScript. - CSS background heroes declared either inline or inside a
<style>block by page builders — the plugin can preload these even though there is no<img>tag at all. - Responsive
<picture>heroes and header logos that act as the mobile LCP element.
What it does to the hero
Once the hero is identified, the plugin makes it unmissable to the browser:
- Adds
loading="eager"and strips anyloading="lazy", so it is never deferred. - Adds
fetchpriority="high"so the browser fetches it ahead of less-important requests. - Injects a
<link rel="preload" as="image">hint into the page head so the download can begin before the browser has even finished parsing the HTML. This is exactly what Lighthouse asks for when it flags "Largest Contentful Paint image was lazily loaded" or "Preload the LCP image."
Manual overrides
Automatic detection is right the overwhelming majority of the time, but you can pin the hero yourself when you know better. Enter one or more image URLs (one per line) as manual LCP targets; these take absolute precedence over detection. Matching is forgiving — it works on a full URL, just the filename, or a wildcard pattern such as */hero-*.webp. You can also add data-sol-lcp to an image or picture source directly in your markup to mark it as the hero explicitly.
If you have our cloud analysis available, Speed of Light will use the LCP element that a real headless browser actually measured on your page, which is even more precise than local detection. If the cloud is unavailable, unlicensed, or the LCP turns out to be text or video, it falls straight back to the local detection above — it never leaves your hero un-optimized because of a cloud hiccup.
CLS: reserving space so nothing jumps
Beyond adding width and height to images, Speed of Light can reserve space for other elements that shift the layout as the page loads — this is cloud-measured. When cloud analysis is enabled, a real browser measures which elements on your page actually caused layout shift, and the plugin then applies the precise fix each one needs: adding real width/height to an unsized image, iframe, or video; reserving a minimum height for a container that grows after load; or eager-loading a specific above-the-fold element that was shifting. Because these fixes come from a real measurement rather than a guess, they only touch the exact elements that were shifting, and the hero image (already handled by the LCP step) is always left alone. With no cloud plan present, this feature does nothing at all, so it can never break a layout on its own.
Iframe and embed optimization
Third-party embeds — YouTube, Vimeo, maps — are some of the heaviest, slowest things you can put on a page. Speed of Light handles them on two levels.
Automatic iframe hints
When lazy-load or dimensions are enabled, iframes without an existing loading attribute get loading="lazy", and known video embeds (YouTube, Vimeo, Dailymotion, Wistia, Loom, and others) that lack sizing get an aspect-ratio:16/9 so they reserve the right space and don't cause layout shift. This aspect-ratio is applied only to recognized video hosts — never to maps, ads, or chat widgets, where 16:9 would be wrong.
Click-to-load facades
Facades go further: instead of loading a full, script-heavy embed on page load, the plugin replaces YouTube and Vimeo iframes with a lightweight preview — the real video thumbnail plus a play button. The genuine embed loads only when the visitor actually clicks it. This can dramatically improve LCP and interactivity on pages with embedded video, because none of the video provider's JavaScript runs until it's wanted. Facades are keyboard-accessible (Enter or Space) and screen-reader labelled. A generic facade for Google Maps embeds is also available and can be extended to other widgets.
Facades are consent-friendly. Before a facade loads the real (tracking) iframe it fires a cancelable sol:facade:beforeload event, so a GDPR consent manager can block the embed until the visitor consents. It fails closed — if that event can't be dispatched, the tracker is not loaded.
Opting individual embeds out
You stay in control of any single element with data attributes in your markup:
<!-- Keep this embed as a full iframe, never a click-to-load facade --> <iframe src="https://www.youtube.com/embed/VIDEO_ID" data-sol-no-facade></iframe> <!-- Never lazy-load this iframe --> <iframe src="…" data-sol-no-lazy></iframe> <!-- Don't apply an aspect-ratio to this embed --> <iframe src="…" data-sol-no-aspect></iframe>
SVG uploads
WordPress blocks SVG uploads by default because a raw SVG can carry embedded scripts. Speed of Light can allow them, but does so carefully: SVG uploads are restricted to users who can already manage the site (administrators), and every uploaded SVG is sanitized — stripped down to a safe allow-list of shape and styling elements, with any javascript:, data:, or external references removed — before it is stored. An SVG that fails sanitization is rejected rather than saved.
SVGs are not raster images, so they are never run through WebP/AVIF conversion and are excluded from dimension and LCP handling. Only enable SVG uploads if you actually need them, and only trust SVGs from sources you control.
A few things this section deliberately does not do
So you know exactly where the boundaries are:
- No image CDN. All conversion and delivery happen on your own server; images are never routed through a third-party image host.
- No restore / no keep-original. Conversion is in-place and permanent. Back up before a first bulk run.
- No
<picture>/srcset generation and no blur-up placeholders. The plugin optimizes and preloads the responsive markup your theme already produces; it doesn't invent new responsive sources.
A sensible default setup: leave conversion on with WebP at quality 82, run one bulk conversion (after a backup), and keep lazy-load, async decoding, dimensions, and LCP optimization enabled. That combination gives you smaller files and the correct loading hints, which is what moves the Core Web Vitals needle. If a specific image or embed ever needs to be left alone, use the data-sol-* attributes above. Still stuck? Email [email protected].
Fonts & Gravatars
Web fonts and commenter avatars are two of the quietest performance drains on a typical WordPress site: both reach out to a third party (Google, Gravatar) on the visitor's connection, and both can delay when readable text and images appear. Speed of Light removes those round-trips by pulling the assets onto your own server and serving them locally. This section explains exactly what each option does, when to use it, and what you will observe once it is on.
Why third-party fonts and avatars slow you down
When a page uses Google Fonts, the visitor's browser must open a fresh connection to fonts.googleapis.com to fetch a stylesheet, then a second connection to fonts.gstatic.com to download the actual font files. Each new host means a DNS lookup, a TLS handshake, and a separate download — all before your text can render in its intended typeface. On slow networks this shows up as either invisible text (FOIT — a blank space where words should be) or a visible flash as fonts swap in.
Commenter Gravatars are the same story in miniature: every avatar on a comment thread is an image request to gravatar.com, a host your page otherwise never touches. Self-hosting both eliminates those extra hosts entirely.
Self-host Google Fonts
When Self-host Google Fonts is on, Speed of Light scans each page's HTML for Google Fonts stylesheets — both <link> tags and CSS @import statements pointing at fonts.googleapis.com. For every one it finds, the plugin:
- Downloads the font's CSS from Google and reads out every
@font-facesource URL. - Downloads the actual font files —
woff2,woff,ttf, orotf— into a local cache folder on your server (underwp-content/cache). - Rewrites the page so the stylesheet and every font URL point at your own domain instead of Google's. The visitor's browser now loads fonts from the same origin as the rest of the page — no extra DNS, no extra handshake, no third-party request.
Only Google Fonts are self-hosted. Fonts served from other CDNs or providers are left untouched — the plugin specifically detects fonts.googleapis.com and fonts.gstatic.com and rewrites only those. It also does not combine or merge multiple Google Fonts stylesheets into one file; each is self-hosted and preloaded individually.
Modern, subsetted font files (not bloated legacy formats)
When fetching the CSS, Speed of Light asks Google as a modern Chrome browser would. This matters: without a modern browser signature, Google's Fonts API returns legacy, unsubsetted TrueType files that can be roughly 32× larger than the modern equivalent. By requesting correctly, the plugin receives compact woff2 faces already sliced into unicode-range subsets — so you self-host the small, efficient files, not the heavy fallbacks.
How the download is kept fast and safe
The font work happens while a page is being built, so the plugin is careful never to let a slow Google response hold up a visitor:
- Fetched once, then cached. The Google stylesheet for a given font set is fetched at most once and stored in a transient for a day. Every later page render reuses that cached CSS instead of hitting Google again. Font files themselves are downloaded once and reused from disk forever after.
- Short timeouts, graceful skip. If Google is slow or unreachable, the fetch times out quickly and the plugin simply leaves that stylesheet as-is rather than stalling the page. A failed fetch is remembered briefly so a broken endpoint is not retried on every render.
- Dynamic pages are never blocked. On pages that are never cached — a WooCommerce or Easy Digital Downloads cart, checkout, or account page, or anything flagged
DONOTCACHEPAGE— the plugin skips the font fetch entirely. Those pages must stay fast and dynamic, so no blocking third-party request is fired on them. - Fail-open by design. If the cache folder cannot be written, or an individual font file fails to download, that font simply keeps its original Google URL. Self-hosting degrades one font at a time; it never breaks the page.
Eliminate invisible text with font-display: swap
Whenever Speed of Light processes a Google Fonts stylesheet, it injects font-display: swap into any @font-face rule that does not already specify a display strategy. This is what ends FOIT (Flash Of Invisible Text): instead of hiding your text while the web font downloads, the browser paints it immediately in a fallback font and swaps to the web font the moment it arrives.
The result is text that is readable from the first paint. This directly helps First Contentful Paint and Largest Contentful Paint, and — because the fallback and final fonts are laid out in the same flow — avoids the jarring blank-then-appear behavior visitors notice on font-heavy sites.
/* Speed of Light adds the swap directive automatically */ @font-face { font-display: swap; font-family: 'Inter'; src: url(/wp-content/cache/sol/fonts/….woff2) format('woff2'); }
Font preload and preconnect
Downloading fonts locally removes the third-party hop; preload goes one step further and tells the browser to start fetching your most important fonts right away, rather than discovering them later while parsing CSS.
With Preload fonts enabled, Speed of Light adds <link rel="preload"> hints for the first font faces referenced on the page, injected just before </head> so the browser sees them as early as possible. The plugin preloads the URLs the page will actually load: when self-hosting is active it preloads your local copies; when it is not, it preloads the real Google URLs the page still references — so a preload hint is never wasted on a file the page won't request. To keep preload focused on what matters (and avoid over-fetching), it preloads at most a couple of faces per stylesheet.
Each preload carries the correct type for the font format (font/woff2, font/woff, font/ttf, or font/otf) and the required crossorigin attribute, so the browser can reuse the preloaded file for the real @font-face request.
If you also use Speed of Light's cloud CSS acceleration, the preload and preconnect hints can be driven by what a real browser render measured actually painting above the fold — a more accurate list than guessing from the stylesheet. When a cloud verdict is available it supersedes the local guess for which faces to preload, and adds preconnect hints for the origins that matter. This runs without any extra blocking fetch and works even when the local self-host and preload toggles are off.
Recommended pairing
- Self-host + swap removes the third-party connection and ends invisible text. This is the biggest win and is safe for virtually every site.
- Preload layers on top: it shortens the time until your headline and body text render in their real font. Turn it on once self-hosting is working.
Gravatar caching
Gravatar caching does for comment avatars what self-hosting does for fonts: it removes the third-party request. When enabled, Speed of Light intercepts each Gravatar image URL on your pages and serves a locally cached copy from your own domain instead of loading it from gravatar.com.
How it works
Intercept the avatar URL
On the front end, the plugin catches every avatar URL WordPress is about to output. Non-Gravatar URLs are left completely alone — only gravatar.com and secure.gravatar.com URLs are handled.
Serve the local copy if it exists
If a cached image for that avatar (matched by its Gravatar hash, requested size, and display options) already sits on disk and is fresh, the plugin rewrites the page to serve that local file. The visitor's browser never contacts Gravatar.
Download in the background on a miss
If no local copy exists yet, the plugin serves the original Gravatar URL for now and queues the download to run on WordPress cron — a few seconds later, off the page-render path. The very first visitor who triggers a new avatar never waits; the local copy is simply ready for everyone after that.
Cached avatars are stored with a 7-day time-to-live. After a week, the local copy is treated as stale and refreshed from Gravatar on the next request, so avatar changes still propagate. Downloads are de-duplicated (the same avatar isn't fetched repeatedly while a request is pending), size-clamped to a sane range, and only accepted if Gravatar actually returns an image — a failed or non-image response is ignored rather than cached.
Gravatar caching is off by default and is auto-recommended by Speed of Light's Smart Configuration when your site would benefit — typically sites with active comment sections. If your pages rarely show Gravatars, leaving it off costs you nothing.
What you'll observe
- Fewer third-party requests. In your browser's network panel, requests to
fonts.googleapis.com,fonts.gstatic.com, andgravatar.comdisappear (or drop sharply) once caches are warm — everything loads from your own domain. - No more invisible text. Text renders immediately in a fallback and swaps to the web font, instead of leaving blank gaps during load.
- Faster real-font paint. Preload and preconnect shorten the delay before your headline and body copy appear in their intended typeface.
- A one-time warm-up. The first few page loads after enabling these options do the downloading. Subsequent visitors get the fully local, cached experience.
If your theme or a plugin loads a Google Font through an unusual mechanism (for example, injected by JavaScript after the page loads, rather than as a normal <link> or CSS @import), Speed of Light may not detect it to self-host it. In that case the font still loads correctly from Google — you simply don't get the self-hosting benefit for that particular font. If you're unsure whether a specific font is being self-hosted, check your browser's network panel for requests to gstatic.com after clearing cache, or reach out to [email protected].
Real-User Monitoring
Real-User Monitoring (RUM) measures how fast your site actually feels to the people visiting it. Instead of a single lab test run from one machine, it collects Core Web Vitals from the real browsers of your real visitors — every device, every connection, every page — and reports them the same way Google does. This is called field data, and it is the data Google uses to judge your site in Search.
Real-User Monitoring is off by default and collects nothing until you deliberately turn it on. When enabled, everything stays on your own server: metrics are posted only to your site's own REST route, and no IP addresses, cookies, or personal data are ever stored.
What it measures
The collector gathers the five metrics that make up Google's Core Web Vitals, using the browser's native PerformanceObserver API:
- LCP — Largest Contentful Paint. Time until the largest visible element (usually the hero image or headline) finishes rendering. Good is under 2.5 seconds.
- INP — Interaction to Next Paint. How quickly the page responds to clicks and taps across the whole visit. Good is under 200 milliseconds.
- CLS — Cumulative Layout Shift. How much the page visually jumps around while loading. Good is under 0.1.
- FCP — First Contentful Paint. Time until the first text or image appears. Good is under 1.8 seconds.
- TTFB — Time to First Byte. Time from the request to the first byte of the response arriving. Good is under 800 milliseconds.
LCP, FCP, and TTFB settle early in the page load, but INP and CLS keep changing for as long as a visitor keeps interacting with the page. Because of this, the collector watches the entire page life and sends the final values only when the visitor leaves the page (on the browser's page-hide signal), so the numbers reflect the complete experience rather than a split-second snapshot.
Reported at p75, exactly like Google
Every metric on the dashboard is reported as the 75th percentile (p75) — the same statistic Google uses for its Core Web Vitals thresholds and the same one shown in the Chrome User Experience Report (CrUX) and PageSpeed Insights. p75 means "75% of your visitors experienced this value or better." This is deliberate: a simple average would produce a number that never lines up with a real Core Web Vitals grade. Because Speed of Light uses p75, you can reconcile the dashboard directly against a PageSpeed Insights report.
INP is reported using the same near-worst-interaction method the official web-vitals library uses, so a single unlucky interaction on a heavily-clicked page does not distort the score. Short interactions as brief as 40 milliseconds are captured, giving you honest responsiveness data rather than only the single slowest click.
Privacy by design
Real-User Monitoring is built to be safe to run on any site, in any jurisdiction, without third-party data-processing agreements. It is 100% first-party and local:
- No third parties. Beacons are posted only to your own site's REST route,
sol/v1/rum. Nothing is ever sent to an outside analytics service or to Speed of Light's own servers. - No IP addresses stored. Visitor IPs are used only in memory for rate-limiting and are never written to the database.
- No cookies, no identity. No cookies are set and no per-visitor identifier is recorded. Individual visitors cannot be distinguished or tracked.
- Query strings stripped. Before a page address is stored, everything after the
?(and any#fragment) is removed, so tracking parameters like?utm_source=…and campaign tags are never retained. - Only coarse device type. Each sample records nothing more than
mobileordesktop— no browser fingerprint, no screen size, no user-agent string. - Rate limited. The receiver accepts at most 20 submissions per minute from any single source and caps each beacon to the known Core Web Vitals, so the table cannot be flooded.
- Automatically pruned. Old data is deleted on a daily schedule. The default retention window is 30 days, after which records are permanently removed.
- Privacy policy suggestion. When RUM is active, the plugin registers a ready-made disclosure in WordPress's built-in Privacy Policy tool that you can insert into your policy page with one click.
The suggested privacy-policy text explains, in plain language, that anonymous Core Web Vitals are collected together with the page address (without query parameters) and a coarse device type, that no personal data, cookies, or IP addresses are stored, and that the data is retained only for a limited period and then deleted automatically. Find it under Settings → Privacy → Privacy Policy Guide.
Logged-in users are excluded
Real-User Monitoring only collects from logged-out visitors. Logged-in users — administrators, editors, shop managers — are served dynamic, un-optimized pages that would pollute your field data and misrepresent what the public actually experiences. Excluding them keeps the numbers representative of your real audience. It also means you will never accidentally record your own admin sessions while working on the site.
Turning it on
Open the Real User Metrics page
In the WordPress admin, go to Speed of Light → Real User Metrics.
Enable collection
On the disabled screen, flip the Enable RUM Collection switch. No data is recorded until you do this.
Let real traffic accumulate
From now on, logged-out visitors' browsers quietly report their Core Web Vitals as they leave each page. The dashboard fills in as samples arrive — high-traffic sites populate within minutes; low-traffic sites take longer.
Read your grades
Use the 1d / 7d / 30d buttons to change the look-back window, then interpret the gauges, distribution, and tables below.
Because the dashboard reports field data from real visitors, an empty or thin dashboard simply means not enough people have visited yet. This is normal on a new or low-traffic site — keep it enabled and check back after your site has received meaningful traffic.
Reading the dashboard
Metric gauges
At the top, four gauges show your site-wide p75 for LCP, INP, CLS, and TTFB, each colored by grade: green (Good), amber (Needs Improvement), or red (Poor). LCP, INP, and CLS are graded against Google's thresholds; TTFB is shown for context. Each card has an i tooltip with the goal and a concrete action to improve it (for example, "enable LCP optimization, optimize images, use a CDN").
LCP grade distribution
A donut chart breaks your LCP samples into Good, Needs Improvement, and Poor buckets with exact counts and percentages. Aim for 75% or more of samples in the green (Good) zone — that is the bar Google sets for passing Core Web Vitals. A large red slice tells you a meaningful share of visitors are having a slow experience, and is your cue to enable LCP optimization and compress images.
Slowest pages
A ranked list of the URLs with the worst average LCP, each with a colored bar and a time in seconds. Bars turn amber over 2.5 seconds and red over 4 seconds. Each entry links to the live page so you can inspect it. This tells you exactly where to focus — usually a handful of image-heavy templates account for most of the problem.
LCP element feedback
This table surfaces the actual elements that visitors' browsers reported as the largest contentful paint — the specific hero image, heading, or banner that defined the load — along with its average LCP and how many times it was seen. This turns an abstract score into a concrete target: if the same background image keeps appearing as a slow LCP element, that image is what to optimize or preload first.
The dashboard and the plugin's other features work together. When RUM shows a slow LCP driven by a particular image, Speed of Light's image optimization and LCP-preload features act directly on that element — so you can measure, fix, and re-measure the improvement from real visitors.
Managing the data
While collection is active, the command bar shows a live sample count for the selected window and a Collecting toggle you can flip off at any time. To wipe the stored history, use the Clear Data button — it removes every recorded metric and cannot be undone.
You do not need to clear data manually for housekeeping: a daily background task automatically deletes anything older than the retention window (30 days by default). If you turn collection off, no new samples are recorded, but existing data remains visible until it ages out or you clear it.
Advanced: adjusting retention
The retention window is filterable for developers who want a shorter or longer history. Add the following to a small custom plugin or your theme's functions.php to keep 14 days instead of 30:
// Keep RUM field data for 14 days instead of the default 30. add_filter( 'sol_rum_retention_days', function() { return 14; } );
A separate filter can enable collection programmatically without the admin toggle, which is useful for staging or fleet management:
// Force RUM collection on regardless of the stored setting. add_filter( 'sol_rum_enabled', '__return_true' );
Because Real-User Monitoring only ever collects from logged-out visitors, browsing your own site while signed in will never produce samples. To confirm collection is working, open your site in a private/incognito window (so you are logged out), visit a few pages, then return to the dashboard — allow a moment for the beacons to arrive as you leave each page.
Database
The Database section gives you a clear, read-only picture of your WordPress database and a set of safe, manual cleanup tools to remove the temporary and unused rows that quietly accumulate over time. Every read is a snapshot — nothing is changed until you deliberately choose an action and confirm it. There is no scheduling, no background job, and no automatic cleanup: you decide what runs and when.
The section is organized into three tabs: Overview (health metrics and charts), Cleanup (the manual maintenance actions), and Autoload (the data WordPress loads into memory on every request).
Not available on multisite. Database Health is intentionally disabled on multisite (network) installs to avoid cross-site database risk. On multisite you will see a notice instead of the tools, and the block is enforced everywhere — both the admin screen and the REST endpoint refuse to run cleanup, so nothing can be deleted even by an API call. It is also disabled on multisite by design and this cannot be toggled on for a network install.
Who can use it
The Database tools require the manage_options capability (normally an administrator). If your account lacks it, the tab shows a permission notice and no data. Every cleanup action is additionally protected by a WordPress security nonce and re-checks your capability at the moment it runs, so a stale or forged request cannot delete anything.
Overview: reading your database at a glance
The Overview tab is 100% read-only. It queries your database for its current state and presents the results as a row of stat cards plus several charts. Nothing here modifies data.
The stat cards
- Total Database Size — the combined size of every table, including both data and indexes. A steadily rising number usually points to accumulating revisions, transients, or plugin log data.
- wp_posts Rows — the total number of rows in the posts table. This includes published content, drafts, revisions, attachments, and auto-drafts, so it is normally much larger than your visible post count.
- wp_postmeta Rows — metadata attached to posts. Page builders and SEO plugins can bloat this table heavily, which is a common cause of slow admin and front-end queries.
- Transients — the count of temporary cached values stored by WordPress and plugins (both site and network-style transients are counted).
- Revisions — the number of saved post/page revisions. WordPress keeps these indefinitely by default.
- Autoloaded Size — the total size of options marked to load on every single page request. This is one of the highest-impact numbers on the page (see the Autoload tab below).
- Table Engine — a summary of which storage engines your tables use, for example
InnoDB (12), MyISAM (1). InnoDB is the modern recommendation because it supports row-level locking and crash recovery. - Fragmentation — the ratio of reported free space to total size, shown as a percentage (or
n/awhen the database does not report it). Fragmentation is unused space left behind by deletions and updates.
The Fragmentation figure is informational only. Speed of Light reports it so you are aware of it, but it does not run OPTIMIZE TABLE or defragment your tables. Defragmentation is a database-server operation best handled by your host or a direct database tool, and running it carelessly can lock tables. If your fragmentation is high, ask your host about optimizing tables during a maintenance window.
The charts
- Size Trend — a line chart of your total database size over the last 14 days. Speed of Light records one size sample per day (keeping roughly the last 30 days), so the trend fills in gradually. A steady climb suggests unchecked growth; a sudden spike usually means a plugin or event added a lot of data at once.
- Storage Engine — a donut chart breaking down how many tables use each engine, so you can see at a glance whether any tables are still on the older MyISAM engine.
- Table Size Breakdown — horizontal bars comparing the five largest tables, so you can immediately see which tables dominate your database.
- Largest Tables — a detailed table of the biggest tables by size (data + index), showing each table's name, approximate row count, size, and storage engine. Use it to spot bloated tables for targeted cleanup.
When the site is brand new to this feature, the Size Trend chart shows “No historical data yet” until at least two daily samples have been recorded. Give it a day or two and the trend line will appear on its own.
Cleanup: safe, manual maintenance
The Cleanup tab lists eight maintenance actions. Each row shows a live count of how many items currently qualify, a short description, and a checkbox to include it in the run. Rows with a count above zero are pre-checked for convenience; you can uncheck anything you do not want to touch. Nothing runs until you submit the form and confirm the browser prompt.
Every action here removes only temporary or unused data. Your live posts, pages, published comments, and settings are never touched. The actions delete through WordPress's own deletion functions where applicable, so related data is cleaned up correctly rather than being ripped out with a blunt query.
What each action removes
- Expired transients — deletes only transient cache rows whose expiry timestamp has already passed (both the value and its matching timeout row, for site and network transients). Unexpired transients are left alone. Expired transients that pile up bloat the options table and slow every request.
- Auto-draft posts — removes unfinished
auto-draftentries for posts and pages (the empty placeholders WordPress creates when you open the editor but never save). - Trashed posts/pages — permanently deletes posts and pages that are already in the Trash. It does not trash anything new; it only empties what you already discarded.
- Spam comments — permanently deletes comments marked as spam.
- Trashed comments — permanently deletes comments already in the Trash.
- Old post revisions — deletes only the oldest revisions beyond a limit you set, keeping the most recent N revisions of each post or page (see below).
- Orphaned postmeta — deletes postmeta rows that point to a post that no longer exists. These serve no purpose and only add weight.
- Orphaned commentmeta — deletes commentmeta rows that point to a comment that no longer exists.
The “keep last N revisions” control
The Old post revisions row includes a number field labeled Keep last N revisions per post/page. Set it to how many recent revisions you want to preserve for each post; only revisions older than that limit are deleted. The value must be between 1 and 50 (the default is 3). For example, with the limit set to 3, a page with 12 revisions keeps its 3 newest and deletes the other 9; a page with only 2 revisions is left completely untouched.
Review the counts
Open the Cleanup tab and look at the count column. Zero means there is nothing to clean for that action right now.
Choose what to run
Leave the pre-checked rows you agree with, uncheck anything you want to keep, and (optionally) adjust the “keep last N revisions” value.
Run and confirm
Click Run Safe Cleanup. A confirmation prompt appears — approve it to proceed. When it finishes, the page reloads and reports how many items were removed in total.
Deletions of trashed posts, trashed comments, and spam comments are permanent — this empties the Trash, it does not move things into it. If there is any content you might still want, restore it from the Trash first, then run cleanup. As with any database maintenance, keeping a recent backup is always good practice.
Autoload: the data behind slow page loads
The Autoload tab focuses on one of the most impactful performance signals in WordPress: autoloaded options. These are option rows flagged to load into memory on every single request, whether the page needs them or not. A bloated autoload set directly raises time-to-first-byte and memory use.
At the top you'll see the Total Autoloaded Size with a health pill:
- Healthy — under about 500 KB.
- Moderate — roughly 500 KB to 1 MB.
- High — above 1 MB, worth investigating.
Below that is a table of the ten largest autoloaded options, each with its size and a best-effort Source label (Theme/Core, Core, a matched plugin slug, or Unknown) so you can tell which plugin or component is responsible.
The Autoload tab is read-only insight, not a delete tool. It shows you which options are heavy so you can act on the root cause — usually by reconfiguring or removing the plugin that created them. Do not delete option rows directly unless you are certain what they belong to; removing the wrong option can break a plugin or your site. Review plugin usage first.
What this section does not do
- It never runs on a schedule and has no background/cron job — every cleanup is triggered by you, by hand.
- It does not run
OPTIMIZE TABLEor defragment tables; fragmentation is reported for awareness only. - It is disabled on multisite installs, and that restriction is enforced on the REST endpoint as well as the admin screen.
- It never touches your live, published content — only expired, trashed, spam, orphaned, or excess-revision data you explicitly select.
Run a Cleanup pass every few weeks, or whenever the Size Trend chart shows steady growth, to keep your database lean. Because everything is manual, confirmed, and limited to disposable data, it is safe to make part of your regular maintenance routine. If you are ever unsure about a specific action or an autoloaded option, reach out at [email protected] before deleting.
Kill Bloat
Kill Bloat switches off the parts of WordPress that most sites never use but that still cost every visitor an HTTP request, a few kilobytes of markup, or a background task. Nothing here rewrites your content or your theme — each option simply stops WordPress from loading a feature you didn't ask for. Every toggle is independent, off by default unless noted, and safe to turn on one at a time.
These are opt-in cleanups, not the caching engine. Turning them all on won't "break" a normal site, but a few interact with less common setups (WooCommerce, pingback-driven workflows, custom heartbeat plugins). Enable, then load your site and admin as usual to confirm nothing you rely on disappeared.
Head & markup cleanup
WordPress prints a handful of <link> and <meta> tags into every page's <head> for features like blog-client discovery and version advertising. Removing them trims your HTML and stops leaking your WordPress version. Each is a separate toggle:
- Remove shortlink — drops the
rel="shortlink"tag from the head and the matchingLink:HTTP header. - Remove generator meta — removes the
<meta name="generator">tag that announces your WordPress version, and blanks the generator string elsewhere (including in feeds). - Remove RSD / WLW links — removes the Really Simple Discovery and Windows Live Writer manifest links, used only by legacy blogging clients.
- Remove rel prev / next — removes the paginated
rel="prev"/rel="next"head links WordPress adds on archives. - Remove REST link tag — removes the REST API discovery
<link>from the head and its response header. The REST API itself keeps working; only the auto-discovery hint is gone.
Response header cleanup
Two toggles strip identifying HTTP response headers from front-end pages (they stay intact inside wp-admin):
- Remove X-Pingback — removes the
X-Pingbackheader that advertises your pingback endpoint. - Remove generator / server headers — removes
X-Powered-By,X-Generator, andX-WordPressstyle headers so responses no longer announce your stack.
Emojis
Disable emojis removes WordPress's emoji support entirely — the inline detection script and stylesheet in <head>, the feed and email emoji filters, and the emoji plugin in the classic editor. Modern browsers and operating systems render emoji natively, so this is one of the safest ways to remove a render-blocking inline script and a small stylesheet from every page.
Dashicons
Disable dashicons removes the WordPress dashicons icon-font stylesheet on the front end — but only for visitors who are not logged in, so your admin bar and any dashicon-dependent admin UI stay intact for editors. If your theme or a front-end plugin genuinely uses dashicons for logged-out visitors, leave this off.
Some page builders and themes render front-end icons with the dashicons font. If icons vanish for logged-out visitors after enabling this, turn it back off — that means your theme depends on it.
Pingbacks & trackbacks
- Disable self-pingbacks — stops WordPress from pinging your own site when you link between your own posts. This removes noisy self-referential comments without affecting incoming pingbacks from other sites.
- Disable pingbacks & trackbacks — closes pings site-wide. This is the broader setting and automatically includes self-pingback suppression, so enabling it makes the self-pingback toggle redundant.
Editor & background activity
These reduce the load your logged-in editors and WordPress's own background systems place on the server:
- Limit revisions — caps stored post revisions at 3 per post going forward, so a heavily edited page can't accumulate dozens of revision rows in the database.
- Throttle Heartbeat — sets the WordPress Heartbeat API interval to 60 seconds, cutting the frequency of admin background AJAX calls (autosave polling, lock checks, dashboard activity) without disabling them.
- Autosave interval (120s) — sets the editor autosave interval to 120 seconds instead of the default 60, halving how often the editor saves drafts in the background. (Skipped if another plugin or your
wp-config.phphas already definedAUTOSAVE_INTERVAL.)
Admin experience
- Hide front-end admin bar — hides the top admin toolbar on the front end for lower-privilege users (subscribers, customers). Administrators still see it, and this never blocks anyone from
wp-admin— it only affects the front-end toolbar. - Hide non-critical admin notices — buffers the admin-notice area and strips routine "plugin promo" style notices while deliberately preserving error and warning notices. If the notice HTML can't be parsed safely, the original notices are shown untouched, so you never lose an error message.
WooCommerce awareness
WooCommerce loads cart scripts, block styles, and shop assets on every page — even a plain blog post or contact page that has no cart or products. These three toggles only take effect when WooCommerce is active, and they only trim assets on pages that aren't part of the shop:
- Disable cart fragments on non-cart pages — removes the
wc-cart-fragmentsscript (the AJAX call that updates a mini-cart count). On sites without a persistent header cart, this eliminates an admin-ajax request on every page load. - Disable WooCommerce assets on non-shop pages — on pages that are not a WooCommerce page, cart, checkout, or account page, this dequeues WooCommerce's general/layout/small-screen styles and its cart, checkout, add-to-cart, and related scripts. Shop, cart, checkout, and account pages keep every asset they need.
- Disable WooCommerce block styles — removes the
wc-block-style/wc-blocks-stylestylesheets on the front end when you aren't using WooCommerce blocks on that page.
The "non-shop pages" logic uses WooCommerce's own conditional tags to decide which pages are shop pages. If you build a custom landing page that shows products or an add-to-cart button outside the standard shop/cart/checkout/account pages, that page will lose the WooCommerce assets it needs. In that case, leave "Disable WooCommerce assets on non-shop pages" off. Cart, checkout, account, and product pages are always protected.
Self-hosting third-party scripts
The Self-host third-party scripts option copies a fixed set of well-known analytics and pixel scripts onto your own server and rewrites the <script src> in your pages to point at the local copy. That removes a DNS lookup and connection to an outside domain for those scripts, and lets them be served from your origin with your own caching.
This is a self-hosting optimizer, not a consent or blocking tool. It does not stop, gate, or ask permission for any tracker — the same analytics run, just served from your domain. It is not a substitute for a cookie-consent or privacy plugin, and you remain responsible for consent and disclosure exactly as before.
Which scripts are covered
Only a fixed, known list is self-hosted. Anything not on this list is left completely untouched:
- Google Analytics —
gtag/js(GA4),analytics.js, and legacyga.js - Google Tag Manager —
gtm.js - Meta / Facebook Pixel —
fbevents.js(US, GB, and generic locales)
Scripts like Google Tag Manager and GA4's gtag/js return different content per container or property ID, so each distinct ?id= is cached to its own local file — separate properties never collide on one shared file.
How it stays fresh (stale-while-revalidate)
Downloads never happen on a visitor's page load. The plugin serves the cached copy immediately and refreshes it in the background:
First encounter
The first time a covered script is seen, there's no local copy yet, so the page keeps the original remote URL and a background download is queued. Your analytics keep working uninterrupted while the copy is fetched.
Local copy served
Once the background download lands, cached pages are re-baked so they reference the local URL. From then on, that script is served from your own domain.
Daily refresh
When a cached copy is more than a day old, the visitor is still served the existing local file instantly, and a fresh copy is fetched in the background for next time. The visitor's request is never blocked on a remote download.
Downloads are validated before they replace anything: the response must be a real 200 with actual JavaScript (an HTML error page or an empty body is rejected), and the file is written atomically so a half-downloaded script never gets served.
To opt a single script out of self-hosting, add data-sol-no-local to its <script> tag and it will be left on its original remote URL:
<!-- This tag is skipped; it keeps loading from the remote domain --> <script data-sol-no-local src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
Optional automatic maintenance
Kill Bloat also includes two housekeeping jobs that run on a daily schedule (at 2 AM in your site's timezone) rather than on any page load:
- Auto-clear transients (on by default) — deletes expired and orphaned transient rows from the options table daily, keeping that table from bloating with stale cached values.
- Auto-optimize database — runs a targeted table optimization once a day, and only on tables that actually need it (those with reclaimable free space, plus MyISAM tables). It skips tables with nothing to gain, so it isn't a blunt full-database rewrite.
These maintenance jobs depend on WordPress cron. If you enable Disable WP-Cron (below), both jobs are automatically switched off and unscheduled, because a disabled cron would leave them enabled-but-dead.
Advanced / infrastructure toggles
These change how WordPress runs at the infrastructure level. They're safe on most hosts but have real trade-offs, so read before enabling:
- Disable WP-Cron — defines
DISABLE_WP_CRON, which stops WordPress from triggering scheduled tasks on page loads. Only enable this if you've set up a real server cron to runwp-cron.php; otherwise scheduled jobs (including the maintenance above, plus other plugins' tasks) will stop running. When enabled, this plugin's own daily maintenance jobs are turned off automatically. - Disable automatic updates — turns off WordPress's automatic core, plugin, and theme updates. Note: if a related "force auto-updates" option is active elsewhere in your settings, that takes precedence and this toggle is ignored so the two can't fight each other.
Disable WP-Cron and Disable automatic updates are for administrators who know their hosting setup. If you don't already run a server-side cron, leave WP-Cron enabled. If you rely on automatic security updates, leave automatic updates enabled.
Recommended starting point
If you just want the safe wins without thinking about edge cases, these are low-risk on almost every site:
- Disable emojis
- Remove shortlink, generator meta, RSD/WLW links, and rel prev/next
- Remove X-Pingback and generator/server headers
- Throttle Heartbeat and set autosave to 120s
- Limit revisions
- Leave Auto-clear transients on (it's on by default)
Add the WooCommerce toggles if you run a store with a mostly-content site around it, and enable Self-host third-party scripts if you use Google Analytics, GTM, or the Meta Pixel. Save the infrastructure toggles (WP-Cron, automatic updates) for last and only if you understand their trade-offs.
Everything in Kill Bloat is reversible: turn any toggle back off and WordPress immediately resumes loading that feature on the next request. Self-hosted script copies simply stop being used, and there's nothing to clean up or migrate.
If a toggle causes an unexpected change on your specific theme or plugin stack, turn it back off and reach out at [email protected] with the toggle name and what you saw.
Compatibility
Speed of Light was built for the way WordPress sites are actually made today — the block editor, block themes, and modern visual builders — not retrofitted onto a legacy optimization engine. That matters because the fastest way to break a site is to strip or delay the very CSS and JavaScript your theme or builder generates on the fly. Speed of Light avoids that by recognizing what you're running and adapting its optimization automatically.
This section explains how that automatic compatibility works, what's protected out of the box, and the one rule you must follow: run only one caching plugin at a time.
Automatic theme & builder detection
On every page, Speed of Light detects your active theme and any page builder that's running, then loads a matching safelist — a list of CSS class names, prefixes, and script handles that its Used/Unused CSS (RUCSS) and JavaScript delay features are told to never touch. The result: aggressive optimization on everything that's safe to optimize, with a protective fence around the dynamic output your builder injects at render time.
Detection is signature-based and doesn't rely on you configuring anything. Themes are matched by their active theme slug; builders are detected by their own constants, classes, or functions (for example Elementor via ELEMENTOR_VERSION, Bricks via BRICKS_VERSION, GreenShift via GREENSHIFT_DIR). When a match is found, the corresponding safelist is merged in before any CSS is trimmed or any script is delayed.
Themes it recognizes
Speed of Light ships built-in awareness for 10+ popular themes, including block-first and modern options:
- Block-native & lightweight: Kadence, GeneratePress, Astra, Neve, Blocksy, OceanWP
- Full block themes: any theme reported by WordPress as a block theme automatically receives a dedicated safelist covering core block classes (navigation, query pagination, social, search, comments, and post blocks)
- Builder-style themes: Divi, Avada, Flatsome
For themes with their own overlapping performance features — such as Blocksy (which serves its own Customizer CSS and can self-host Google Fonts), Kadence, and Neve (built-in critical CSS) — Speed of Light surfaces plain-language guidance in the Compatibility report so you don't accidentally run two critical-CSS generators against each other. In those cases the advice is simple: enable Critical CSS or Used/Unused CSS last, and preview your key templates.
Builders it recognizes
Speed of Light ships matching safelists (and, where needed, file-level exclusions) for 5+ major builders:
- Elementor — safelists its element, widget, column, section, sticky, and popup classes; excludes its common and Pro CSS from processing
- GreenShift — safelists its container, row, column, tabs, and accordion classes so per-block dynamic CSS survives
- Oxygen — safelisted and excluded so its unified CSS isn't disturbed
- Bricks — safelisted (its own CSS optimization is respected)
- Beaver Builder — safelisted for its dynamic CSS
- Divi — safelisted, with its highly dynamic inline design CSS excluded from processing
Beyond these, Speed of Light also carries WooCommerce and Breakdance safelists that engage automatically when those are present.
Nothing to configure. You don't select your theme or builder anywhere. Detection and safelisting happen on their own, on every request. Open the Compatibility tab to see exactly what Speed of Light detected on your site and any tailored recommendations for it.
What "safe" optimization looks like
The safelists don't turn optimization off — they make it precise. On a builder site you still get full page caching, CSS/JS minification, JavaScript delay/defer, local Google Fonts, and image conversion. Only the two features that reason about which CSS a page needs — Critical CSS and Used/Unused (RUCSS) — are constrained by the safelist, because those are the only ones that could remove a builder's dynamically-added styles.
JavaScript that's never delayed
Speed of Light's JavaScript delay improves load time by holding non-critical scripts until user interaction — but some scripts must run immediately or the page breaks. Those are protected automatically:
- jQuery core and migrate — matched by handle, path, filename, and the official jQuery CDN, so themes and plugins that depend on jQuery keep working
- Builder frontend scripts — for example Elementor's interaction scripts are recognized as plugin-critical and left alone in safe mode
- Advertising loaders — never delayed, so ad revenue isn't affected
- Already-
asyncor already-deferscripts — left untouched in every mode to preserve execution order - Scripts on WooCommerce transactional pages — cart, checkout, and My Account pages skip JavaScript delay entirely, because payment fields, AJAX cart updates, and address validation must run right away
- The admin toolbar view — while the WordPress admin bar is showing (a logged-in view), JavaScript delay is skipped so the toolbar's own scripts function
You're never locked into one intensity. JavaScript delay runs in safe, aggressive, or custom mode, and you can add your own "never delay" exclusions on top of the built-in list.
WooCommerce & EDD: carts and checkout are never cached
E-commerce needs the opposite of caching on personal, stateful pages. Speed of Light handles this the correct way: the storefront is cached; anything tied to a shopper's session is not. When it sees a WooCommerce or Easy Digital Downloads (EDD) session or cart cookie on a request, it serves that page fresh instead of from cache — so a customer never sees someone else's cart or a stale checkout.
Speed of Light's cache is anonymous-only by design. It caches pages for visitors who have no session and no login. It does not build per-user or personalized caches — logged-in users, commenters, and shoppers with active carts are simply bypassed and served live. The page cache watches for 20+ cookie patterns and bypasses on any match, including:
woocommerce_items_in_cart,woocommerce_cart_hash, and thewp_woocommerce_session_prefixedd_items_in_cartand the broaderedd_prefixwordpress_logged_in,wordpress_sec, andwordpress_no_cachewp-postpass(password-protected posts) andcomment_author- Session cookies from common commerce and membership add-ons (YITH wishlist/recently-viewed, WP eCommerce, Ecwid, Bookly, and others)
You can add your own bypass patterns under the cache exclusion settings — your custom cookies are merged with the built-in list, never replace it. So the WooCommerce and EDD protections above always stay in force.
On the CSS side, a dedicated WooCommerce safelist protects storefront classes (notices, cart, checkout, account, variation, price, add-to-cart, and form classes) from Used/Unused CSS removal, and JavaScript delay is skipped on cart, checkout, and account pages as described above. Together these mean you can safely run full optimization on a store without breaking the purchase flow.
Run only ONE caching plugin
This is the single most important compatibility rule. Two caching or optimization plugins fighting over the same page cause race conditions, doubled minification, broken layouts, and unpredictable behavior that's painful to diagnose. Speed of Light is a complete solution — page cache, asset optimization, fonts, images, object cache, and CDN integration — so it should be the only caching plugin you run.
Deactivate other caching plugins before relying on Speed of Light. Speed of Light scans your active plugins and flags conflicts in the Compatibility report. If it finds a competing cache or optimization plugin, deactivate that plugin first — don't try to run both with features "split" between them.
What the conflict scanner watches for
Speed of Light checks your active plugins against a list of 15+ known caching and optimization tools and grades each match by severity:
- Critical (deactivate before use): WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, WP Fastest Cache, FlyingPress, NitroPack, Swift Performance, Cache Enabler, and Breeze (Cloudways). Running any of these alongside Speed of Light's cache will conflict and can break your site.
- Warning (choose who does what): Autoptimize, Perfmatters, SG Optimizer, and WP Optimize — these overlap on CSS/JS or image optimization, so double-processing is the risk.
- Info (generally safe / complementary): the Cloudflare plugin, Redis Object Cache, Object Cache Pro, Query Monitor, and image optimizers like EWWW, ShortPixel, and Imagify. For images, still pick one optimizer — Speed of Light or the other tool — not both.
How to resolve a flagged conflict
Open the Compatibility report
Go to the Compatibility tab in Speed of Light. Any detected caching or optimization plugin appears with its severity and a short recommendation.
Deactivate the other cache plugin
For any Critical match, go to Plugins in WordPress and deactivate the competing caching plugin. Keep only Speed of Light as your caching layer.
Clear caches, then re-check
Purge Speed of Light's cache (and Cloudflare's, if connected) so the first pages rebuild cleanly. Reload the Compatibility report and confirm the conflict is gone.
Split image optimizers deliberately
If you keep a dedicated image optimizer (EWWW, ShortPixel, Imagify), turn off Speed of Light's image conversion — or vice-versa — so the two don't both rewrite the same files.
What plays nicely alongside Speed of Light
A few tools are complementary rather than conflicting and can stay active:
- Redis Object Cache / Object Cache Pro — these accelerate database queries, which is a different layer from page caching. (Speed of Light also includes its own native Redis object cache if you prefer to use it instead.)
- The Cloudflare plugin — Speed of Light integrates with Cloudflare's modern Cache Rules for CDN and edge caching. Just make sure you're not duplicating page-rule behavior across both.
- Query Monitor — a developer diagnostics tool with no caching of its own; safe to keep active while debugging.
Still unsure about a specific setup?
If you're running a theme, builder, or plugin combination not covered above and want a second opinion before enabling the CSS-reasoning features, reach out to [email protected] with a note about your theme and builder. The safest universal path is: enable page caching, minification, JavaScript delay, fonts, and images first; then enable Critical CSS and Used/Unused CSS last, previewing your most important templates as you go.
WP-CLI, REST API & hooks
Everything Speed of Light does from its admin screens is also scriptable. The same operations are exposed three ways: a wp sol WP-CLI command family for shell and deployment scripts, a first-party REST API under /wp-json/sol/v1/ for programmatic and remote control, and a broad set of WordPress action/filter hooks plus wp-config.php constants for developers who want to change behavior in code. There is also a per-post meta box for turning two optimizations off on individual posts. This section documents all four.
Automation is optional. If you never touch the CLI, REST API, or hooks, the plugin runs exactly the same — these interfaces exist for agencies, CI/CD pipelines, and developers who want scripted or fine-grained control.
WP-CLI: the wp sol commands
If you have WP-CLI installed on your host, the plugin registers a wp sol command namespace. These are ideal for deploy hooks (clear the cache after a release), cron jobs (warm the cache on a schedule), and bulk maintenance. Every command prints a plain success or error line so you can check exit status in scripts.
# ---- Page cache ------------------------------------------------- wp sol clear # clear the entire site (or network) cache wp sol clear --ids=1,2,3 # clear only these post IDs wp sol clear --urls=example.com/about/ # clear a specific URL wp sol clear --urls=example.com/blog/* # wildcard: a page and all its subpages wp sol clear --sites=1,2,3 # multisite: clear specific blog IDs # ---- Preload (cache warming) ----------------------------------- wp sol preload # queue every public URL for warming wp sol preload --turbo # queue in turbo mode (higher throughput) wp sol preload --run # process the queue in THIS process until done wp sol preload --run --batch=500 --delay=1 wp sol preload --run --max-seconds=120 --sleep=2 # ---- Remove Unused CSS ----------------------------------------- wp sol rucss clear # delete all generated used-CSS files + DB records wp sol rucss stats # entries, completed, failed, total CSS size # ---- JavaScript minify ----------------------------------------- wp sol js-minify clear # delete all minified JS files # ---- Object cache (Redis) -------------------------------------- wp sol object-cache status # drop-in state, connection, hit ratio wp sol object-cache enable # install the object-cache.php drop-in wp sol object-cache disable # remove the drop-in wp sol object-cache update-dropin # refresh drop-in to the current version wp sol object-cache flush # flush all object-cache entries wp sol object-cache diagnostics # low-level Redis/PHP diagnostics table wp sol object-cache groups # list global + non-persistent cache groups
Command reference
wp sol clear— With no options, clears the whole page cache (the message says "Network cache cleared" on multisite, "Site cache cleared" otherwise).--idsand--urlsaccept comma-separated lists and can be combined. A--urlsvalue may include or omit the scheme and may end in*to match a path prefix.--sitestargets specific blog IDs on a multisite network.wp sol preload— Queues public URLs for cache warming. By default it only queues and returns how many URLs were queued; pass--runto have the current process actually work through the queue. Tuning flags:--batch(URLs per batch),--delay(seconds between scheduled batches),--concurrency(parallel requests),--chunk(URLs per runtime chunk),--runtime(worker window), and, only relevant with--run,--max-seconds(stop after N seconds) and--sleep(pause between batch runs).--turboenables turbo mode. When--runfinishes it prints a "Cached / Pending / Failed" summary.wp sol rucss clear/stats—clearwipes all Remove Unused CSS output and the database records that track it, so CSS is regenerated on the next visit.statsprints total entries, how many completed, how many failed, and the total stored CSS size.wp sol js-minify clear— Deletes every minified JavaScript file the plugin has generated; they are rebuilt on demand.wp sol object-cache <action>— Manages the native Redis object-cache drop-in.status(the default action) prints a table with drop-in validity, connection state, backend, serializer, hit ratio, and hits/misses.enableinstalls the drop-in,disableremoves it,update-dropinrefreshes it to the current plugin version,flushempties the object cache,diagnosticsprints a detailed Redis/PHP table (compression, key prefix, Redis memory, eviction policy, evicted keys, and more), andgroupslists the global and non-persistent cache groups.
The object cache is a WordPress persistent object cache — values persist across requests in Redis. Each request opens a fresh Redis connection (there is no connection pooling); this keeps behavior predictable across PHP-FPM workers.
On a multisite network the object-cache drop-in is network-wide. From the REST API only a Network Super Admin may change it. The wp sol object-cache commands run with whatever WP-CLI user context you invoke them under, so run them as a super admin on multisite.
REST API: /wp-json/sol/v1/
The plugin registers a first-party REST namespace, sol/v1. Every route requires the manage_options capability (on multisite, the object-cache write routes additionally require a Network Super Admin), so requests must be authenticated as an administrator — for example with an application password over HTTPS. There is no separate API key: authentication and authorization are WordPress's own.
Example: read live stats
# Authenticate with a WordPress application password (admin user). curl https://example.com/wp-json/sol/v1/stats \ --user "admin:xxxx xxxx xxxx xxxx xxxx xxxx"
A GET to a read route returns JSON; a POST (or DELETE, where noted) performs the action. All bodies are JSON.
Available routes
- Settings —
GET /settingsreads the full configuration;POST /settingssaves a patch through the same sanitizers the admin UI uses.GET /settings/exportandPOST /settings/importmove configuration between sites;POST /settings/resetrestores defaults. - Stats —
GET /statsreturns current cache and performance statistics. - Cache purge —
POST /cache/purgeclears the page cache.POST /critical-css/purgeandGET /critical-css/statsmanage Critical CSS. - Preload —
POST /preloadstarts cache warming;POST /preload/stopstops it.GET /cached-urlslists what is currently cached. - Edge (Cloudflare) —
POST /edge/purge,POST /edge/deploy,POST /edge/zones, and the asset-rule deploy/remove routes drive the Cloudflare Cache Rules integration. - Images —
POST /images/bulk-compressstarts a background compression run;GET /images/compress-status,GET /images/stats,POST /images/compress-stop,POST /images/compress-rescan, andPOST /images/repairmanage it. Image conversion is local and in-place. - RUM —
DELETE /rumclears stored Real User Monitoring data. (RUM is opt-in, entirely first-party, and stores no IP or personal data.) - Database —
POST /db-cleanruns the manual database cleanup. It never runsOPTIMIZE TABLEand is disabled on multisite. - Object cache —
GET /object-cachereads status;POST /object-cache/enable,/disable,/flush,/config, plusGET /object-cache/testandGET /object-cache/metrics. - Insights & health —
GET /insights,DELETE /insights,POST /insights/scan,POST /insights/fix,POST /insights/fix-all,POST /insights/autopilot,POST /insights/undo; plusGET /health,GET /health/check,GET /logs/DELETE /logs, andPOST /score/boost.
Every sol/v1 route enforces manage_options. Never expose these credentials to a browser or commit them to a repository — treat an application password like the admin password it stands in for.
Hooks: actions & filters
For developers, the plugin fires a large number of WordPress hooks — roughly 43 do_action hooks and 131 apply_filters filters across its modules. They let you react to cache events and adjust behavior without editing plugin files. A few commonly useful ones:
- Actions —
sol_site_cache_cleared,sol_page_cache_cleared,sol_page_cache_created,sol_complete_cache_cleared, andsol_db_after_cleanuplet you hook into cache and cleanup lifecycle events. - Cache-behavior filters —
sol_bypass_cache(force a request to skip the page cache),sol_cacheable_query_params(allow specific query strings to be cached),sol_cache_allowed_hosts, andsol_user_can_clear_cache. - Asset filters —
sol_assets_should_defer_script,sol_assets_should_delay_script,sol_js_delay_plugin_exclusions, andsol_preconnect_hostsfine-tune JavaScript delay/defer and resource hints. (JavaScript is delayed, deferred, and minified — never combined or concatenated.) - Preload filters —
sol_preload_concurrency,sol_preload_chunk_size,sol_preload_runtime,sol_preload_request_args, andsol_preload_user_agenttune the cache-warming worker (these are what the matchingwp sol preloadflags set at runtime). - Database-cleanup filters —
sol_db_allowed_cleanup_items,sol_db_cleanup_capability, andsol_enable_db_cleaner.
Constants: configure in wp-config.php
You can override or lock down behavior with PHP constants defined in wp-config.php, above the "That's all, stop editing" line. Define them before WordPress loads the plugin:
// In wp-config.php define( 'SOL_DISABLE_ASSETS', true ); // emergency off for CSS/JS optimization define( 'SOL_CACHE_DIR', '/var/cache/sol' ); // custom page-cache directory define( 'SOL_REDIS_HOST', '127.0.0.1' ); define( 'SOL_REDIS_PORT', 6379 ); define( 'SOL_REDIS_DISABLED', true ); // keep drop-in but skip Redis
Useful constants
SOL_DISABLE_ASSETS— Set totrueto completely turn off CSS/JS optimization. This is the recommended emergency switch if a theme or plugin misbehaves under asset optimization.SOL_DISABLE_REST_API/SOL_DISABLE_XMLRPC— Reflected in the plugin's diagnostics as hardening toggles for those WordPress endpoints.SOL_CACHE_DIR(and the relatedSOL_CACHE_URL,SOL_IMAGES_DIR,SOL_IMAGES_URL,SOL_SETTINGS_DIR) — Relocate where the plugin stores its cache, converted images, and settings files. Useful on hosts with unusual filesystem layouts.SOL_CACHE_ALLOWED_HOSTS— Restricts which hostnames the page cache will serve, useful for staging/production domain setups.SOL_REDIS_HOST,SOL_REDIS_PORT,SOL_REDIS_PATH,SOL_REDIS_SCHEME,SOL_REDIS_DATABASE,SOL_REDIS_PREFIX,SOL_REDIS_DISABLED— Configure the object-cache connection. These take precedence over the widely-usedWP_REDIS_*constants (which the drop-in also honors as a fallback, so it works out of the box on many managed hosts). When you enable the object cache from the UI, the plugin writes theSOL_REDIS_*block intowp-config.phpfor you inside a clearly marked, auto-managed section.SOL_CLOUD_ENDPOINT— Overrides the cloud render service endpoint (advanced/self-hosted setups).
Because SOL_REDIS_* constants win over the plugin's stored settings, defining them in wp-config.php is the reliable way to pin a Redis connection in code — for example to keep every environment on the same socket or host regardless of what the UI shows.
Per-post meta box: opt a single post out
Some pages have CSS or JavaScript that does not survive aggressive optimization. Rather than disabling a feature site-wide, you can turn two optimizations off for just one post or page. When editing any public post type, look for the SOL Optimization box in the sidebar.
Open the editor
Edit the post or page that needs an exception.
Find "SOL Optimization"
It appears in the sidebar (side context) of the editor for every public post type.
Check what to disable
Disable Remove Unused CSS keeps the full stylesheet for this post; Disable JS Minify serves this post's JavaScript unminified. Each checkbox affects this post only.
Update the post
Save/Update as usual. The exception takes effect on the next request to that page.
These per-post toggles are surgical: they affect only the post you set them on, and only the two features shown. Every other optimization — page caching, HTML minify, image optimization, LCP hints, and so on — keeps working normally on that page. Use them to fix a one-off layout or script glitch without weakening performance across the rest of the site.
If you hit a case the CLI, REST API, hooks, constants, or per-post box don't cover, contact support at [email protected].
Troubleshooting
Most Speed of Light problems fall into a handful of well-understood categories, and nearly all of them have a one- or two-step fix. Work through the section that matches your symptom. If you get stuck, the built-in Health tab (covered at the end) usually names the exact thing to fix, and every self-check ends with a one-click path to support.
Before you change anything, try the single most effective diagnostic there is: open your site in a private / incognito window. A logged-in browser session is deliberately never served from cache, so "it looks the same" while logged in tells you nothing. A private window shows you exactly what an anonymous visitor sees.
The dashboard looks broken or is showing an old version
The Speed of Light settings screen is a compiled React app served from the plugin's assets/admin-ui/dist folder. If you updated the plugin (or rebuilt it during development) and the dashboard now renders half-styled, throws console errors, or shows controls that no longer match the documentation, your browser or a leftover build directory is serving stale JavaScript.
Hard-refresh the page
Reload the settings screen with a cache-busting refresh (Cmd/Ctrl + Shift + R). This alone fixes the majority of "old dashboard" reports, because the browser is simply holding an out-of-date bundle.
Delete leftover build artifacts
If you built the plugin yourself or upgraded over a development copy, an orphaned dist/ or .vite/ cache directory can shadow the shipped bundle. Delete any stray .vite/ folder and let the plugin's own assets/admin-ui/dist be the only build present, then hard-refresh again.
Clear a page/object cache in front of wp-admin
If you run a CDN or a second optimizer that (incorrectly) caches admin assets, purge it so the fresh bundle can be fetched.
Note that this concerns only the admin UI. It never affects the pages your visitors are served — the front-end cache and the dashboard bundle are completely independent.
Pages are not being cached
Speed of Light only caches anonymous page views. Logged-in users, commenters, and anyone carrying a session cookie are intentionally bypassed and served a fresh, uncached page every time — the cache is never personalized per user, it is simply skipped for them. So the first rule is always: test in a private window while logged out. If pages still are not caching, walk through the checklist below in order.
1. Is page caching actually turned on?
Open the Cache tab and confirm Local Cache is enabled. When it is off, no HTML is written to disk at all — that is a valid configuration, but it means there is nothing to serve.
2. Is the drop-in installed and owned by Speed of Light?
Page caching serves early by installing a WordPress drop-in at wp-content/advanced-cache.php and setting define( 'WP_CACHE', true ) in wp-config.php. If another caching plugin already owns advanced-cache.php, Speed of Light will not overwrite it. The Health tab reports this precisely ("controlled by another plugin"). The fix is to deactivate the other page-cache plugin, then toggle Local Cache off and on so Speed of Light can install its own drop-in.
3. Is the cache directory writable?
The web server must be able to write to the cache folder (by default wp-content/cache/sol). If permissions are wrong, nothing can be stored. Set the folder so the web-server user can write to it (typically 755 on the directory).
4. Are you (or a cookie) triggering the logged-in bypass?
Even in a "logged-out" browser, a leftover cookie can force a bypass. Speed of Light skips the cache whenever it sees a logged-in state, a preview or Customizer request, or any of 20+ session/commerce cookies — including WordPress login and password-protect cookies, WooCommerce and EDD cart/session cookies, comment-author cookies, and common membership, Jetpack, and firewall cookies. It also bypasses any request that carries a nocache or cachebuster query flag. To rule this out, test in a fresh private window with no extensions injecting cookies.
5. Is the page on an exclusion list, or flagged DONOTCACHEPAGE?
Under Cache settings you can exclude specific post IDs, post types, URL paths, query strings, and cookies. An excluded page is completely untouched — it is not cached, not preloaded, and not sent for cloud optimization. Check those lists if a specific page refuses to cache. Separately, WordPress core, WooCommerce, and many plugins set the DONOTCACHEPAGE constant on pages that must stay dynamic (cart, checkout, account, etc.); Speed of Light always honors it and will never cache such a page. That is correct behavior, not a bug.
Running two page-cache or "all-in-one optimization" plugins at once is the single most common cause of both "pages won't cache" and "pages look broken." Pick one and deactivate the other. The Health tab's Conflicts group detects the well-known offenders and flags them by name.
The preloader reports 403 errors
When Speed of Light warms your cache, it fetches each URL over a loopback request to your own site. If a security layer answers that request with HTTP 403 Forbidden, the page cannot be warmed. Speed of Light inspects the blocked response and tells you who returned it — Cloudflare (with a Ray ID), ModSecurity (an origin WAF), Wordfence, or your origin web server (nginx / Apache / LiteSpeed) — so you can allowlist the right layer instead of guessing.
The preload requests are authenticated with a rotating one-time token and are tagged with an X-SOL-Preload: 1 request header. To let them through:
- Behind a firewall/WAF plugin (Wordfence, a ModSecurity ruleset, etc.): allowlist your own server's outbound IP, or create an exception for requests carrying the
X-SOL-Preloadheader, so the loopback is not treated as a hostile bot. - Behind Cloudflare or another edge: make sure your origin allows same-server loopback requests. If the edge is challenging the preloader, add a rule that skips bot/JS challenges for your origin IP.
- Rate-limited: lower the preload batch size and add a small delay so you do not trip a per-minute request limit (see the WP-CLI options below).
There is no "Bypass CDN during preload" switch — an earlier build had one and it was removed because it could not reliably reach the origin from behind a CDN. Normal preloading warms the cache correctly once the blocking layer is allowlisted; the right fix is always to let the authenticated loopback through, not to route around the CDN.
A page looks unstyled after "Remove Unused CSS"
Remove Unused CSS (RUCSS) ships each page with only the CSS that page actually used at render time. Because it works from what the cloud render saw, styles that appear only after a later interaction — a class toggled by JavaScript, an accordion, a mega-menu, a cookie banner, styles injected by a third-party widget — can occasionally be stripped, leaving an element unstyled. Speed of Light already carries a large automatic safelist plus per-plugin/theme compatibility rules, but you can teach it about a class it missed.
Add the class to your RUCSS safelist
Open the CSS/RUCSS settings and add the missing selector or class prefix to the safelist (one entry per line). Safelisted selectors are always kept, even when they were not detected on the first render.
Or disable RUCSS for that one page
Every post and page has a Speed of Light meta box in the editor with a Disable Remove Unused CSS checkbox (and a matching one for JS minification). Tick it to serve that single page with its full, untouched CSS while every other page keeps the optimization.
Clear the stored Used-CSS and re-warm
Used CSS is cached per page, so your change only takes effect after the old copy is cleared. Run the command below, then reload the page (or preload) to regenerate it.
# Clear all stored Used-CSS so pages regenerate with your new safelist wp sol rucss clear # Then re-warm the cache (queue a preload, or run it right away) wp sol preload --run
If styling still looks off after clearing, set the CSS/assets performance mode to a safer level (the levels are off, safe, balanced, aggressive, and custom) — a safer mode keeps more CSS at the cost of a little size.
Enabling the object cache made the plugin look deactivated
This is a known WordPress footgun, and Speed of Light already guards against it. WordPress caches its own plugin-state options (active_plugins and alloptions). The instant a persistent object cache comes online, WordPress can read a stale pre-cache copy of that list on the very next request and momentarily behave as if Speed of Light — or another plugin — was switched off.
Speed of Light's object-cache drop-in defuses this by dropping a one-shot .sol-oc-flush-once marker when it installs, so the freshly-installed cache flushes those plugin-state options on its first load, before WordPress reads them, and it flushes the live cache as a belt-and-suspenders measure. On the 1.3.x drop-in this happens automatically.
If you enabled the object cache on an older build and hit this, the recovery is trivial and harmless:
- Simply reactivate Speed of Light from the Plugins screen (deactivate, then activate). This rewrites the plugin-state cache from the real database.
- Or toggle the object cache off and on once from the Object Cache tab, which re-runs the drop-in installer and its flush.
Nothing was actually deactivated — only a cached copy of the plugin list was briefly stale. Your settings, license, and cached pages are untouched, and after the flush everything reads correctly.
Redis / object cache will not connect
When the object cache is enabled but cannot reach its backend, Speed of Light does not break your site — it silently falls back to a non-persistent, per-request cache, so pages still load. The Health tab surfaces this as an attention item (a warning, not a failure) with the backend's own error message.
The object cache connects to Redis fresh on each request. It reads its connection from your saved Object Cache settings (or from WP_REDIS_* / SOL_REDIS_* constants in wp-config.php). Both TCP and UNIX-socket transports are supported:
- TCP: defaults to host
127.0.0.1on port6379. Setrediss/sslas the scheme for a TLS connection. - UNIX socket: point the connection at a socket path. For a co-located Redis this is faster than TCP. When you leave the default localhost/6379 config and no explicit host is set, Speed of Light will even auto-detect a common local socket and use it, falling back to TCP if the socket cannot be opened.
If the Health tab shows the object cache as "enabled but not connecting," check the following:
- Redis is actually running on the host (
redis-cli pingshould returnPONG). - The host, port, socket path, password, and scheme in Object Cache settings are correct.
- The web-server user has permission to open the socket file, if you configured one.
- If Redis blocks WP-CLI connections, keep
WP_REDIS_DISABLED=1set for CLI runs so command-line calls do not hang.
If none of that applies, you can simply disable the object cache; page caching and every optimization keep working without it.
Edge cache (Cloudflare) is not getting a hit
Speed of Light's edge integration deploys Cloudflare Cache Rules so anonymous HTML can be served straight from Cloudflare's edge. Cloudflare is the only supported CDN, and the integration uses the modern Cache Rules API — there is no APO, Argo, Tiered Cache, or dev-mode automation involved. If edge hits are not happening, the usual causes are:
- Rules not deployed yet. Connecting your Zone ID and API credentials is not enough — you must open the Edge Cache tab and click Deploy to push the rules. The Health tab flags "connected but rules not deployed" specifically.
- A dynamic cookie on the request. The edge deliberately bypasses caching for logged-in and commerce/session cookies (the same families the page cache bypasses), so a request carrying one of those will always miss at the edge. Test anonymously.
- The very first render is served un-optimized, then re-cached. On the first hit for a URL, the page may be served before cloud optimization has been applied. Speed of Light does not pin that un-optimized version at the edge — it applies the optimization in the background, purges the edge for that URL the moment the optimized version is ready, and only then advertises a long edge-cache lifetime. So an early miss (or a briefly un-optimized hit) is expected; give it a moment, or run a preload, and subsequent requests hit the optimized, edge-cached copy.
Behind Cloudflare, a loopback health probe from your own server can look like a cache MISS even when real visitors are being served from cache, because the probe's request key differs from a real HTTPS visitor's. Speed of Light accounts for this by also checking whether a cache file already exists on disk, so a working site is not falsely reported as broken.
Using the Health tab
The Health tab runs a lightweight, one-click self-check and reports each result in plain English — healthy, needs attention, or a problem — with a specific fix for anything that is not green. It is deliberately cheap: it reads your settings, does one loopback probe of your homepage, and makes a single cloud reachability ping. It never renders a page or exposes any private endpoint details.
The checks are grouped so you can see at a glance which area needs attention:
- Plugin core — is the page-cache drop-in installed and owned by Speed of Light, is
WP_CACHEset, is the cache directory writable, is a homepage cache actually being served, is the object cache connected, are background tasks advancing, and is your license active. Only a genuinely broken page cache (missing/foreign drop-in,WP_CACHEoff, or an unwritable cache directory) turns the overall report red; optional features being off or a soft cloud/license state are at most a warning. - Cloud API — reachability, renderer readiness, license link, and authentication, reported as status words only. If your host firewall blocks outbound HTTPS the cloud may read as unreachable; local caching is unaffected.
- Optimization — confirms each optional feature (Critical CSS, Remove Unused CSS, LCP optimizer, image optimization, Real User Monitoring) is either off, or on and its module actually loaded. A feature that is off is perfectly healthy.
- Conflicts — detects other active caching/optimization plugins that would fight Speed of Light.
- Edge — whether Cloudflare edge caching is off, connected, or connected with rules deployed.
About background tasks: Speed of Light self-drives preloading and scheduled purges over lightweight background loopbacks and does not depend on WP-Cron. So DISABLE_WP_CRON being true is normal and never, by itself, a problem — the Health check only warns when there is queued work that has genuinely stalled with nothing driving it. If you ever see that warning, open the Speed of Light dashboard once to nudge the worker, and make sure your host or firewall is not blocking outbound loopback requests to your own site.
Clearing the cache from the command line
When you just need a clean slate, WP-CLI is the fastest path. These commands work whether or not the dashboard loads:
# Clear the entire site cache wp sol clear # Clear specific posts by ID wp sol clear --ids=1,2,3 # Clear specific URLs (wildcards supported) wp sol clear --urls=example.com/blog/* # Re-warm the cache after clearing wp sol preload --run --batch=500 --delay=1
On a slow host or behind a strict firewall, lower --batch and raise --delay to keep the preloader from tripping rate limits, then let it run.
Still stuck?
If the Health tab is green but a specific page still misbehaves, isolate the cause: disable Remove Unused CSS and JS optimization for that one page from its editor meta box, clear its cache, and reload. If the problem disappears, re-enable the features one at a time to find which one to safelist. When you need a hand, every Health self-check ends with a one-click path to support — or email us at [email protected] with your site URL and the exact Health tab output, and we will point you at the fix.
Frequently asked questions
What exactly does Speed of Light (Pro) do?
- Local disk cache — a full-page HTML cache served by an
advanced-cache.phpdrop-in before WordPress fully boots, with pre-compressed gzip/Brotli copies. - Optional Cloudflare edge cache — full-page HTML caching at Cloudflare's global edge for low TTFB worldwide.
- Optimized origin render — the fresh page WordPress produces, already optimized on the way out.
On top of caching it layers asset optimization (CSS/JS minify, Remove Unused CSS, Critical CSS, LCP preload, font self-hosting, interaction-based JS delay), a native Redis object cache, image conversion, Real User Monitoring, and a set-and-forget "Smart Configuration" that picks safe defaults for your host and stack.
Do I need Redis to use it?
If you do have Redis, the plugin ships a native object-cache drop-in with its own dashboard (hit ratio, memory, ops/sec, evictions) and value compression. If Redis ever becomes unavailable, the drop-in degrades gracefully to a per-request in-memory cache — it never takes your site down.
Does it work without Cloudflare?
Is the Cloudflare edge caching really free?
s-maxage directive plus 103 Early Hints.Is my Cloudflare API key safe? What permissions does the token need?
Zone:Cache Purge and Zone:Read — the minimum needed to deploy cache rules and purge the edge. That token cannot touch DNS, billing, or other zones.The token is entered in a masked password field and stored as a WordPress option in your own database. Keep in mind it is stored in plaintext (like most cache-plugin CDN credentials), so restrict database and admin access accordingly, and revoke/rotate the token in Cloudflare if you ever remove it.
Will it break my theme, WooCommerce, Elementor, or other page builders?
For WooCommerce specifically, cart/checkout/account cookies force a bypass (those pages are never cached), and WooCommerce, Stripe, PayPal, and other transactional scripts are on a built-in "never delay" list. CSS/JS optimization also runs in a safe mode by default that leaves plugin-critical scripts eager. If you ever hit an edge case, you can raise or lower the aggressiveness per feature or exclude a specific page.
Does it cache pages for logged-in users?
This keeps private/dynamic content correct (an admin never sees a visitor's cached page, and vice-versa). Asset optimizations like CSS/JS minify and JS delay are also skipped while the admin bar is showing, so the toolbar and editing tools are never touched.
What about my ads and analytics — will delaying JavaScript break them?
adsbygoogle, googlesyndication, doubleclick.net, googletag, pubads), Google Tag Manager and Analytics (googletagmanager, gtag, google-analytics), and the Meta/Facebook pixel (fbevents.js).These stay eager in every mode, because delaying an ad loader until user interaction would stop ads from rendering for passive visitors and directly cut revenue. Measurement and consent scripts stay intact.
Can I run it alongside another caching plugin (WP Rocket, W3 Total Cache, LiteSpeed, WP Super Cache)?
Is multisite supported?
Will it slow down my admin (wp-admin)?
Which image formats does it produce, and does it use an image CDN?
cwebp shell fallback for WebP). If a converted file isn't actually smaller than the original, it keeps the original and discards the conversion.There is no off-site image CDN and no external image-delivery host — your images stay served from your own domain. It also does not generate <picture>/srcset variants or blur-up placeholders; instead it adds width/height, lazy-loading, and fetchpriority hints to reduce layout shift and prioritize the hero image.
Does it phone home or collect personal data? Is Real User Monitoring private?
PerformanceObserver + sendBeacon to gather LCP, INP, CLS, FCP, and TTFB, and stores them in a table in your own database — nothing is sent to a third party.- It does not store IP addresses or any personal data. The visitor IP is used only transiently, hashed, for per-minute rate-limiting.
- It strips the query string and fragment from URLs before storing, so tracking parameters are never retained.
- Data is auto-pruned on a schedule and reported at the 75th percentile.
The optional cloud optimization sends only the page URL to be rendered (authenticated by your license); it does not upload visitor data.
How do I stop a specific page from being cached or optimized?
Two ways:
- By URL — add the path to the excluded-URLs list in settings. An excluded page skips the entire pipeline: no HTML caching, no CSS/JS optimization, nothing. Sensible defaults already exclude
/wp-admin/,/wp-login.php,/cart/,/checkout/,/my-account/,/wp-json/, feeds, sitemaps, and search. - Per-post — each post/page has a Speed of Light meta box in the editor with checkboxes to Disable Remove Unused CSS and Disable JS Minify for that single post.
Is there WP-CLI support?
wp sol namespace. Examples:wp sol clear # purge the whole cache wp sol clear --ids=1,2,3 # purge specific posts wp sol clear --urls=example.com/blog/* wp sol preload --run --batch=500 # warm the cache wp sol rucss clear # clear used-CSS wp sol rucss stats wp sol js-minify clear wp sol object-cache status wp sol object-cache flush wp sol object-cache diagnostics
The preloader also supports batch size and delay flags for large sites.
What PHP and WordPress versions are required?
wp-content/cache/sol/ and wp-content/advanced-cache.php so the disk-cache drop-in can be installed. If PHP is below 7.4 the plugin shows an admin notice and stays inactive rather than risking your site.How does the Cloudflare integration keep the edge cache fresh?
Does the cache expire on its own and leave visitors on stale pages?
wp-cron.php is recommended so preloading advances on a fixed schedule.What is Autopilot, and can I turn it off?
Are there "one-click / aggressive / developer" modes to choose from?
- CSS / asset optimization has a performance mode:
off/safe/balanced/aggressive/custom. - JS delay has a mode:
safe/aggressive/custom.
Defaults are the conservative options, so most people never change them.
How does its object cache differ from a page cache and the edge cache?
- Page (disk) cache stores the finished HTML of a whole page, served before WordPress fully boots — the biggest single latency win.
- Edge cache (Cloudflare) stores that same HTML at Cloudflare's global network, so distant visitors hit a nearby copy instead of your origin.
- Object cache (Redis) caches WordPress's internal pieces — database query results, options, transients — so uncached/dynamic requests (and logged-in views, the admin, WooCommerce) run with far fewer database queries.
Page/edge cache speeds up anonymous visitors; the object cache speeds up everything else that can't be page-cached.
Is the Redis object cache using persistent/pooled connections?
connect(), phpredis with a Predis fallback); it does not use persistent socket pooling. It also isolates its keys with an install-specific namespace and only ever flushes its own keys, so it never wipes another app's Redis data.What is Remove Unused CSS (RUCSS) and is it risky?
How does font optimization work — does it combine my Google Fonts?
preload/preconnect hints with font-display so text renders immediately. This removes the third-party request to Google and improves privacy and LCP. It does not merge multiple font stylesheets into one combined CSS file — the win comes from self-hosting plus preload, not concatenation.Does it combine/concatenate my JavaScript?
What does the Database Health / cleanup feature do?
OPTIMIZE TABLE, and the whole module is disabled on multisite to avoid cross-site risk.Which cookies trigger a cache bypass?
wordpress_logged_in, wordpress_sec), password-protected posts (wp-postpass), commenters (comment_author), WooCommerce (woocommerce_*, wp_woocommerce_session_), Easy Digital Downloads (edd_*), YITH, Jetpack, Ecwid, Bookly, and more. You can add your own patterns in settings. Any matching cookie means that visitor always gets a fresh, uncached page.How do I uninstall cleanly, and what does it leave behind?
sol_used_css, sol_rum_metrics, sol_logs), clears its cache files, and removes the drop-ins it installed — advanced-cache.php, the Redis object-cache.php, and the object-cache config file — only when it confirms those files are its own (by signature). It also removes its one-shot flush marker. After uninstall, your site falls back to standard WordPress behavior with no orphaned drop-ins.How is it licensed and what are the plans?
A big convenience: the cloud Core Web Vitals features authenticate automatically with your license — there is no separate API key to paste. The cloud is an accelerator, not a hard dependency: every optimization falls back to on-server generation if the cloud is ever slow or unreachable, so your site can never break.
What is Performance Insights ("the detective")?
Can't find what you need?
Reach a real human on the Speed of Light team. We'll help you get the most out of every millisecond.