Features
xSpeed Hub Pricing Docs Blog Scan
Appearance
Get Plugin

How to remove unused CSS

Optimization 8 min

Most themes ship a stylesheet where any given page uses a fraction of the rules. Prune the rest per URL — with a safelist and real-visitor learning to keep it honest.

A page-builder theme can ship hundreds of kilobytes of CSS covering every block, layout and widget it supports — while any given page uses a small fraction of it. Unused CSS works out which rules a URL actually needs and serves only those. It’s typically the single biggest transfer reduction available on this panel, and also the one that most deserves checking before you trust it.

Where to find it

  1. In your WordPress admin, click xSpeed Cache in the left menu.
  2. In the xSpeed Cache sidebar, open the Optimization group.
  3. Click the CSS & JavaScript card, then the Unused CSS tab.

Shortcut: open wp-admin/admin.php?page=xspeed#/performance/minify/unused directly.

⭐ This tab is part of xSpeed Pro.

Unused CSS status and generation

  1. Status — whether it’s on, how many URLs are cached, and total size.
  2. Build one now (optional) — capture in the browser, or generate on the server, instead of waiting for an automatic build.

Settings at a glance

SettingDefaultWhat it does
Enable Unused CSSOffReplace stylesheets with pruned inline CSS.
Learn from real visitorsOffRefine the analysis using real page views.
Rendering service URLEmptyA self-hosted renderer to measure pages with. Empty = rendering is brokered through the xSpeed Hub.
Rendering service tokenEmptyBearer token for that service, when it requires one.
Never prune these selectorsEmptySafelist of selector fragments to always keep.

How it works

Analysis runs per URL, like Critical CSS, because different templates use different rules.

Builds happen on their own. A visitor landing on a page with no pruned CSS queues one, and WordPress cron builds it a few minutes later — you don’t have to press anything. The button on the panel is for building a page right now instead of waiting. Entries rebuild themselves when your theme, a plugin, or a plugin version changes, so they don’t go stale as the site does — and Purge all leaves them alone, because they aren’t a render cache.

A doubtful entry is refused, not trusted. An entry that’s stale, empty or implausibly small is rejected rather than used, so a bad build can’t strip every stylesheet from a page and leave it unstyled. And only one CSS strategy owns a page: where Unused CSS has a usable entry, Critical CSS stands down.

When enabled, each run of adjacent stylesheets is replaced, in place, by an inline <style> block holding just that run’s pruned CSS. That “in place” detail matters more than it sounds: nothing moves relative to the inline blocks around it, so the cascade is preserved. CSS is order-sensitive, and a tool that reorders your styles will break things in ways that are miserable to debug. This one doesn’t.

Some stylesheets are never touched, and knowing the list saves you time:

  • Sheets marked data-xspeed-keep
  • Sheets marked data-no-optimize
  • Disabled sheets
  • Sheets carrying an integrity attribute
  • Cross-origin stylesheets

The integrity and cross-origin exclusions are structural: pruning a sheet with a subresource-integrity hash would invalidate it, and a cross-origin sheet isn’t xSpeed’s to rewrite.


Two ways to generate

Same choice as Critical CSS:

Capture in browserGenerate on server
HowOpens the page in a hidden frame in this tab and records which rules it actually usesHas a real browser render the page — brokered by the Hub, or through your own Rendering service URL — and prunes from what it measured
AccuracySees what JavaScript builds and what really renders, at your screen sizeAlso a real render
NeedsThe tab kept open while it runsNothing — works headlessly
Fails whenA security plugin blocks framingThe render fails — reported, never swapped for a guess

Capture in browser runs in your browser, so it needs the tab left open. Generate on server is the choice when your browser can’t frame the page. External tooling can also push pruned CSS in via the upload endpoint if you generate it in a build pipeline.

⚠️ A failed render is reported, not replaced with a guess. If a render fails on a licensed install, xSpeed won’t quietly fall back to reading the served markup. The failure shows on the panel, from WP-CLI and in the activity log, and automatic builds pause for a cooldown before trying again.


Settings

The Unused CSS settings

  1. Enable Unused CSS — the master switch.
  2. Learn from real visitors — refine using real page views.
  3. Rendering service URL — measure pages in a real browser instead of reading markup.
  4. Rendering service token — bearer token, if your service needs one.
  5. Never prune these selectors — the safelist.

Learn from real visitors

Off by default. It loads a small front-end script reporting which selectors each page actually uses, from real browsers. Reports carry selector names only — never CSS, and never visitor data.

The safety mechanism is worth understanding: several visitors have to agree before anything is rebuilt. One visitor’s session doesn’t rewrite your CSS. That guards against a single unusual page view — someone with an extension injecting markup, say — poisoning the analysis.

It samples every page view rather than a subset, and it’s a separate beacon from the Critical CSS fold beacon. They measure different questions: this one asks “what does this page use at all?”, the fold beacon asks “what paints first?”

Rendering service URL

The problem this solves is the same one the safelist exists for, attacked from the other side. Reading a page’s HTML cannot tell you what CSS it uses after someone clicks: a class JavaScript adds on interaction is missing from the served markup in exactly the way a class no element ever carries is missing. The two are indistinguishable, so pruning either keeps dead CSS or breaks a working control.

Point this at a rendering service and each page is opened in a real browser and measured for real — including the states it reaches on interaction — before anything is pruned.

You usually don’t need to set it. With an active licence, generation is brokered through the xSpeed Hub on its own — using your licence, so no Hub connection is required — and the credential that reaches the renderer stays on the Hub instead of on every install. Fill this in only to point at a self-hosted renderer, which takes precedence when set. Rendering service token is the bearer token for that service, if it needs one.

💡 Leaving it empty is the normal setup. The Hub brokers rendering for any licensed install, so there’s nothing to configure. What changed in this release is failure: a render that fails is now reported and builds pause briefly, rather than the page being quietly pruned from its markup instead — which lost accuracy on interactive states without telling anyone. The safelist below still covers the classes a render can’t reach.

Never prune these selectors

The safelist, and the setting that will save you. It takes selector fragments, one per line, matched as case-insensitive substrings of the whole selector. So short entries are broad — cart also keeps .woocommerce-cart-form.

There’s already a built-in list covering common runtime states. Use this field for classes a plugin or theme only adds after your own JavaScript runs — because static analysis can’t see them. A modal that gets .is-open on click, a menu that gets .active on scroll, a class applied by a third-party widget: all invisible to analysis, all pruned, all broken.

⚠️ This is where problems come from, and they’re not obvious. A page looks fine on load and breaks only when someone interacts — opens a menu, adds to cart, triggers a modal. Test those interactions after enabling, not just the initial render. When something breaks, add its class to the safelist rather than turning the feature off.


A sensible workflow

  1. Turn on Enable Unused CSS. Pages start building on their own as visitors reach them.
  2. Build your key templates now instead of waiting — homepage, a post, a page, and anything with interactive UI — then check them in a private window.
  3. Interact with everything — menus, modals, tabs, carts, accordions. This is the step people skip.
  4. Safelist anything that broke, then rebuild that page.
  5. Optionally enable real-visitor learning to refine over time.