Asset Optimizer
Premium performance — faster pages in minutes.
Combine and minify enqueued CSS and JavaScript to cut HTTP requests and speed up load times — the asset optimization premium performance plugins charge for, free.
Asset Optimizer combines your front-end CSS and JavaScript into single cached files, cutting the number of HTTP requests a visitor's browser has to make. It detects every enqueued asset on your site and lets you pick exactly which ones to merge, so you stay in control.
Activate it from Unyson+ → Extensions, then open its settings, which has a CSS tab and a JavaScript tab.
Combining and minifying CSS/JS cuts the number and size of downloads, which speeds up loading — but always measure before and after with real tools, because aggressive combining can occasionally backfire. Pair it with browser caching and right-sized images for the biggest wins. See Performance.
How it discovers assets
To know what to combine, Asset Optimizer needs to see your front end render. It does a one-time
internal request to your homepage and remembers the stylesheets and scripts that load. To re-scan
later (after activating a new plugin or theme, or if your site is behind a full-page cache like WP
Engine), visit any page with ?fw_asset_optimizer_discover=1 appended to the URL, then return to
the settings and refresh.
CSS
Every stylesheet detected on the front end is listed and checked by default. Uncheck any you don't want merged, those keep loading as separate requests; the rest are combined into one cached file.
The list is shown in the order the combined file uses, with the theme stylesheets floated to the end (parent then child) so the cascade is preserved, your child theme's CSS still has the authority to override the framework and shortcode styles.
Finding things in a long list
A real site registers a couple of hundred stylesheets, so the list is folded into collapsible groups — WordPress core, the framework, shortcodes, each extension, animation effects, the parent and child themes, generated CSS, and other plugins. Groups come from each asset's path, which is what actually identifies its owner, rather than its handle name, which a plugin can choose freely.
Each group header carries a tri-state checkbox (checked, partially checked, unchecked) that toggles the whole group, and a count of how many of its stylesheets are being combined. Above the list are Check all / Uncheck all, a filter box that matches on handle or path, and an Only unchecked toggle for reviewing just the exclusions. A group with nothing selected is greyed out, so a glance down the collapsed list shows what is being left out without opening anything.
Groups are ordered by where their first stylesheet falls in the cascade, and handles keep their cascade order inside each group — the list is grouped, never re-sorted, so it still reads top-to-bottom the way the combined file is assembled.
:::note Animation effects The Animation Engine's effect partials get their own group. Their classes are applied by JavaScript after the page loads, which makes them the easiest thing on the page to break by excluding the wrong handle — so they are kept visible as one set rather than scattered through the list. :::
Removing unused CSS
Combining solves the number of requests; it does not make the CSS smaller. A typical page uses under 15% of the stylesheet it downloads — the framework and every shortcode ship all the variants they can render, and a page uses one of them.
Switch on Remove unused CSS and each page gets its own copy of the bundle with the rules nothing on it can match taken out. WordPress's own inline block styles are trimmed at the same time — on a page built with the page builder rather than blocks those are typically 70% unused. On a measured site the homepage went from 39.3 KiB to 16.3 KiB gzipped with no rendering change at all. The purged copy is generated the first time a page is viewed and cached from then on, so visitors never wait for it.
It needs Combine CSS on. It works with either CSS delivery setting: with Inline the trimmed stylesheet is what gets written into the page, not the full one.
:::note The Live Editor is never affected Editing a page with the Live Editor switches the whole optimizer off for that one request, so the editor always gets the complete stylesheet. It has to: the editor builds its interface after the page loads, so none of it is visible to the scan that decides which rules are in use, and without this it would come up unstyled. Your visitors still get the optimized page as normal. :::
:::caution Test it before you rely on it This is the one setting here whose mistakes are quiet. A rule removed in error does not raise an error: it shows up as a button with no hover, or a mobile menu that opens wrong, on a page nobody re-tested. After switching it on, click through a few pages — open the menu, expand an accordion, hover the buttons, tab through the links.
If something looks wrong, add ?fw_ao_nopurge=1 to the URL. That serves the full stylesheet for
that one request, so you can tell immediately whether this feature is the cause.
:::
The safelist
A page is scanned as it arrives, and a lot of CSS exists for moments that scan never sees — a menu
opened, a row hovered, a slider started, an element scrolled into view. Those are protected
automatically: state classes (is-, has-, active, open…), every hover and focus rule,
animation and slider classes, the Animation Engine's effects, and anything driven by an attribute
such as aria-expanded.
Add to Never remove (safelist) only for class names your own JavaScript adds. One per line; a plain entry keeps any selector containing that text, and slashes make it a regular expression:
promo-banner
/^\.seasonal-/
Move late CSS into the head
Some elements write their own CSS while the page is being built, and it ends up at the bottom of the page — after the browser has already drawn them. Rules that decide how wide an element is, or whether it is centred, then arrive too late, and the element visibly jumps into place.
On a measured page the hero was drawn against the left edge and then jumped 360 pixels across when its CSS finally loaded. That single jump accounted for the page's entire layout-shift score:
| Layout shift score | |
|---|---|
| Off | 0.1468 |
| On | 0.0000 |
Switching this on collects those blocks and puts them in the page head, where they belong, so everything is drawn in its final position the first time. The page looks exactly the same once loaded — it just stops moving while it gets there.
:::tip If a tool blames your fonts Page-speed tools often attribute this kind of shift to a web font, because fonts are the usual culprit. A quick way to tell them apart: a font swap moves things downwards, as text re-wraps. If your content is jumping sideways, it is late CSS, not the font. :::
Preloading the hero image
A slow "largest contentful paint" usually is not about file size — it is about when the browser finds out the image exists. Normally it cannot know until it has downloaded the stylesheet, built the layout and reached that element.
Preload the hero image tells it straight away. It also removes the loading="lazy" attribute
from that one image, which matters more than it sounds: the page builder marks every image lazy,
including the one in your hero — so the browser was being told to delay the very element the
score is measured on.
On a test page over a typical mobile connection this took the largest contentful paint from 3.2 seconds to 2.3 — nearly a second, with no change to the page itself.
The hero is the first reasonably-sized image after your header, ignoring logos and icons. If a page has no such image, nothing is added.
:::tip Measure it on a real connection On a local site this setting appears to do nothing — there is no network latency to hide, so the order things are fetched barely matters. Test it with throttling on, or on the live site. :::
Inline or linked? (the render-blocking question)
A linked stylesheet is a render-blocking request: the browser will not paint until it has it. Tools flag this, and switching CSS delivery to Inline makes the warning go away, because the CSS travels inside the page.
Whether that is actually an improvement depends on how people use your site. Measured on a real page over a throttled connection:
| First view | Every later page view | |
|---|---|---|
| Linked file | 30.2 KB | 12.0 KB (CSS already cached) |
| Inline | 30.2 KB | 30.2 KB (CSS re-sent every time) |
The first view costs the same either way, and inline painted about 50–85 ms sooner because it saves a round trip. But the stylesheet can no longer be cached, so every subsequent page costs an extra 18 KB.
So: Inline suits a site where most visits are a single page — a landing page or a campaign page. Linked file suits a site people browse, where the stylesheet is fetched once and reused, and it is the default for that reason. Either way the difference is small; this is a refinement, not a rescue.
Browser cache for static files
The combined and purged files are already cached for a year (below). Everything else your site serves — your uploaded images, your fonts, the stylesheets and scripts that belong to plugins and themes — is left to whatever your host decides, and hosts are usually conservative. On one measured site that was seven days for images and fonts, and no caching instruction at all for some plugin scripts, so returning visitors re-downloaded them every time.
Browser cache for static files extends the same one-year policy to all of it, by adding a block
to wp-content/.htaccess. Anything already in that file is left untouched, and switching the option
off removes only the block it added.
Be clear about what this is worth: it only helps people who come back. A first-time visitor downloads everything either way, so this will not rescue a slow page — it is a small, free improvement for repeat visits.
:::note When could this serve someone a stale file?
Almost never, by design. Plugin and theme files carry a version in their URL that changes when they
update, and WordPress never reuses an upload filename — it adds -1 instead. Both cases produce a
new address, so the old cached copy is simply never asked for again.
The exception is replacing a file in place — over FTP, or with a "replace media" plugin. Someone
who already has the old one keeps it until they hard-refresh. This is also why the rule deliberately
stops short of marking files immutable: that would tell browsers to ignore even a hard refresh.
:::
This needs Apache or LiteSpeed. On nginx the setting shows you the equivalent configuration to paste
into your server config instead, since an .htaccess there would do nothing at all.
Caching of generated files
The combined and purged files are cached by the browser for a year. This is safe because their
filenames contain a hash of their contents — change anything and the filename changes, so a visitor
can never be served a stale file. Nothing to configure; an .htaccess is written alongside them.
If your server is nginx rather than Apache, that file is ignored and the same rule belongs in your server config:
location ~* /uploads/unysonplus/asset-optimizer/.*\.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
Stylesheets that arrive late
Some stylesheets are not requested until the page is already being built — an element registers its own CSS at the moment it renders, which is after the bundle has been assembled. Those used to stay outside the bundle permanently, loading as separate requests on every single view. On one measured site that was seven stylesheets, about 35 KB, every time.
Asset Optimizer now remembers what each page actually used and folds those stylesheets into the bundle from the next view onwards. Nothing to switch on. The only thing to know is that the very first view of a page after a change still shows them separately — that view is what teaches it. Load the page twice before judging the result.
The memory is per URL, lasts a week, and is cleared automatically whenever the bundles are — when you switch theme, activate or deactivate a plugin, run an update, or save these settings.
Generate missing image sizes
WordPress normally saves several versions of every image you upload, so a browser can fetch one that suits the space it has to fill. If those in-between versions were never made, there is nothing to choose from and every visitor downloads the full-size original, however small it appears on the page.
This is the usual state of images brought in by the Site Converter, which makes only two sizes on purpose because resizing is the slowest part of a conversion.
Generate missing image sizes fills in what is absent. On a measured page a slot 1024 pixels wide was downloading the 1440-wide original at 91.9 KB; with the missing sizes in place the browser took the 1024-wide file at 67.7 KB instead — 26% less, for exactly the same picture on screen.
The work happens gradually as pages are viewed, one image per view, so no page stalls waiting for it; a page with several unfinished images catches up over its next few visits. An image that cannot be processed is noted and quietly skipped rather than retried forever.
:::tip Already have a lot of images?
This fixes them as they are viewed, which is fine for a normal site. To do the whole library at once
instead, regenerate your thumbnails — wp media regenerate on the command line, or any of the
regenerate-thumbnails plugins.
:::
Image quality
The theme generates its own cropped versions of your images. Until now those were written at whatever quality the server happened to default to — on a real site that came out around 88, which is higher than WordPress's own default and roughly twice the file size a good WebP needs.
Image quality sets it explicitly. 82 is WordPress's default and the recommended choice; 80 is a little smaller again and hard to tell apart; 75 is noticeably smaller and worth checking on a photo you care about first. On a real image from a live site, 75 produced a crop 17% smaller than 82 with no visible difference at normal viewing size.
The saving appears gradually. Each crop is re-made the first time it is shown after you change the setting, not all at once, so a busy site spreads the work over its next few page views rather than doing it in one go.
PNG images are unaffected — PNG is lossless, so there is no quality to trade.
:::note Above 80 is wasted Each crop is also saved as WebP, and visitors get whichever file is smaller. The WebP stops getting bigger above about 80, so raising the setting past that only inflates a JPEG nobody downloads. Below about 70 the JPEG becomes the smaller of the two and is served instead — still correct, still the smallest file, but you are looking at JPEG compression at that point, which is where visible artefacts start. :::
JavaScript
Scripts detected on the front end are listed too, but combining JS is more delicate than CSS, so the defaults are conservative:
- Only first-party scripts (the UnysonPlus plugin and your active theme) are checked by default. Tick a third-party script to include it as well.
- Unsafe scripts are skipped automatically, even if checked. Only local footer scripts with no
async/deferstrategy and no inline or localized data are ever merged. WordPress core, external / CDN scripts, and anything carrying per-request data are always left alone.
Two switches tune the combined bundle:
| Option | What it does |
|---|---|
| Defer combined script | Adds defer to the bundle so it loads without blocking render. Safe because the bundle is self-contained and dependency-ordered. |
| Minify combined script | Strips comments and redundant whitespace (conservative, string/template/regex-aware). Most scripts are already minified, so the saving is usually small, experimental. |
Typical workflow
- Activate Asset Optimizer and open its settings (the asset lists populate from a homepage scan).
- On the CSS tab, leave the stylesheets you want merged checked; uncheck any to keep separate.
- On the JavaScript tab, the first-party scripts are pre-selected; tick any safe third-party scripts you also want combined, and optionally enable Defer.
- Save. The combined, cached files are served on the front end; re-scan with
?fw_asset_optimizer_discover=1whenever your asset set changes.
Because the theme already compiles its design into one generated stylesheet and the page builder emits clean markup, Asset Optimizer has little to clean up, it mostly folds the remaining plugin/element stylesheets and scripts into one request each.