To eliminate render-blocking resources in WordPress, first find which plugin or theme adds each blocking file, stop the files loading on pages that do not use them, then let your page builder and one cache plugin defer the JavaScript, inline the critical CSS and remove unused CSS from what is left. Doing it in that order matters: a cache plugin can hide the symptom, but only unloading removes the cause, and it is what also fixes "reduce unused CSS" and "reduce unused JavaScript". This guide walks through each step with the plugin settings and the code. For how render blocking works on any site, read our guide to eliminating render-blocking resources.
Quick answer
- Measure one page per template with the free unused code checker and the speed test, and note the files named.
- Trace each file to its source from the URL (
/wp-content/plugins/{name}/or/themes/{name}/) and itsidattribute, which is the WordPress handle plus-cssor-js. - Unload files where they are not used, with an asset manager or a few lines of code: a form plugin only on the contact page, WooCommerce styles only on shop pages.
- Switch on your builder's own asset settings (Elementor, Divi) so it ships only the CSS and JavaScript of the widgets on each page.
- Let one cache plugin defer JavaScript, load CSS without blocking and remove unused CSS, one switch at a time.
- Test menus, sliders, forms and checkout after each change, then re-run the checker.
Why WordPress sites have so much render-blocking CSS and JavaScript
WordPress themes and plugins add their files with wp_enqueue_style() and wp_enqueue_script(). Most enqueue on every page, because the plugin cannot know which pages will use it: a slider plugin loads on the blog, a form plugin on the product pages, WooCommerce styles on the About page. Stylesheets in the <head> block rendering by definition, and many older plugins also put their scripts in the <head> without defer.
Page builders add a second layer: CSS and JavaScript for every widget they offer, whether the page uses it or not. On a typical theme-plus-builder site, most of the CSS a page downloads is never used on that page. The waste is not in any single file; it is in files loading where they are not needed.
Step 1: Find the files and where they come from
The unused code checker loads the page in Chromium with code coverage switched on and lists every script and stylesheet by unused bytes, first-party and third-party apart, with Google's estimate of the time you would save. The speed test's render-blocking finding lists the files that delay the first paint.
To trace a file to its source, look at its URL and its id:
<link rel="stylesheet" id="contact-form-7-css"
href="/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=5.9" media="all">
<script id="swiper-js" src="/wp-content/plugins/some-slider/assets/swiper.min.js?ver=8.4"></script>- The folder after
/plugins/or/themes/names the plugin or theme. - The
idis the handle WordPress registered, plus-cssor-js. Here the handles arecontact-form-7andswiper. You need the handle to unload the file in code. - Files under
/wp-includes/come from WordPress core, such aswp-block-library(block styles) andjquery-core.
The WordPress theme & plugin detector adds up what each plugin loads on a page, and what each WordPress plugin costs your page explains how to read it.
Step 2: Unload files on pages that do not need them
This is the step that actually removes unused CSS and JavaScript, rather than hiding it. Two ways:
With an asset manager plugin
Asset CleanUp and Perfmatters (its Script Manager) list every file each page loads, grouped by plugin, and let you switch a file off per page, per post type or everywhere except chosen pages. Typical rules:
- Contact form plugin: off everywhere except the contact page.
- Slider plugin: only on the home page.
- WooCommerce styles and scripts: off on blog posts and static pages (keep them on shop, product, cart, checkout and account pages).
- Social sharing and comment plugins: only on posts.
Test each rule on the pages it touches. An asset manager is one more plugin, but it runs its logic before the page is built and usually pays for itself.
With code
If you prefer no extra plugin, dequeue by handle in a child theme's functions.php or a small must-use plugin. Use a late priority so the plugin has already enqueued its files:
<?php
// wp-content/mu-plugins/unload-assets.php
add_action('wp_enqueue_scripts', function () {
// Contact Form 7 only on the contact page
if (!is_page('contact')) {
wp_dequeue_style('contact-form-7');
wp_dequeue_script('contact-form-7');
}
// Slider only on the front page
if (!is_front_page()) {
wp_dequeue_style('swiper');
wp_dequeue_script('swiper');
}
}, 100);Some plugins offer their own switch. Contact Form 7 has the filters wpcf7_load_js and wpcf7_load_css, and WooCommerce's stylesheets can be removed with the woocommerce_enqueue_styles filter, for example on a site that styles the shop entirely in its theme.
WordPress core block styles
WordPress loads wp-block-library, the CSS for all core blocks. Block themes load only the styles of the blocks each page uses. Classic themes can opt in with one line (WordPress 5.8 and later):
add_filter('should_load_separate_core_block_assets', '__return_true');If the site does not use the block editor at all, for example a site built entirely in a page builder, you can dequeue wp-block-library instead. Check a few posts first: content made with blocks will lose its styling.
Step 3: Turn on your page builder's asset settings
Builders ship the most unused CSS, and each has settings that load only what a page's widgets need:
- Elementor: Settings → Performance (in older versions, Features): Optimized CSS Loading, Improved Asset Loading and the related switches. See Elementor performance.
- Divi: Theme Options → General → Performance: Dynamic CSS, Dynamic JavaScript Libraries, Critical CSS and Defer jQuery and jQuery Migrate. See Divi performance settings.
Builder settings come before the cache plugin, because they reduce what exists; the cache plugin then has less to process.
Step 4: Defer JavaScript and load CSS without blocking
Now let one cache or performance plugin handle delivery. The settings have different names but do the same four jobs:
| Job | What it does | WP Rocket | LiteSpeed Cache |
|---|---|---|---|
| Defer JavaScript | Scripts download in parallel and run after parsing | Load JavaScript deferred | Load JS Deferred → Deferred |
| Delay JavaScript | Chosen scripts run only after the first interaction | Delay JavaScript execution | Load JS Deferred → Delayed |
| Load CSS without blocking | Inline critical CSS, load the full CSS later | Optimize CSS delivery → Load CSS asynchronously | Load CSS Asynchronously |
| Remove unused CSS | Replace stylesheets with only the rules each page uses | Optimize CSS delivery → Remove Unused CSS | Generate UCSS |
Our WP Rocket settings and LiteSpeed Cache settings guides give the values we recommend and the exclusions each needs. Other plugins, such as Autoptimize, offer similar options under their own names.
Three rules keep this safe:
- Defer before delay. Deferring rarely breaks anything; delaying breaks anything that must work before a tap, such as a cookie banner or an above-the-fold slider. For how deferring works and why inline scripts break, read async vs defer.
- Remove unused CSS needs a safelist. Styles for menus, pop-ups and elements added after interaction are "unused" when the page loads. Add their classes to the plugin's safelist when something loses its styling.
- One cache plugin only. Two plugins that both minify, defer and cache fight over the same files.
If you manage the theme yourself, WordPress 6.3 and later can defer your own scripts at the source with the strategy argument of wp_enqueue_script(), which is cleaner than rewriting HTML afterwards.
Step 5: Minify CSS and JavaScript
Minifying strips whitespace and comments. In WordPress it is a checkbox in every cache plugin ("Minify CSS files", "CSS Minify") and in some builders. It saves a little on files that are not already minified; most plugin files ship minified (.min.css, .min.js) already. It does not reduce unused CSS; it only makes the same rules smaller. Minify in one place only: minifying in the builder and again in the cache plugin is a common source of broken scripts.
Leave "combine" options off on HTTP/2 and HTTP/3 hosts. Combining helped on HTTP/1.1, where every file cost a connection; today it mostly produces one big file that changes whenever any part changes, so browsers cannot cache the parts separately.
Step 6: Verify and watch for breakage
After each change:
- Clear the cache plugin's cache and any CDN cache.
- Load the page as a logged-out visitor (logged-in admins often bypass optimisation).
- Click through menus, sliders, tabs, forms, the cart and the checkout. Open the browser console and look for errors.
- Re-run the unused code checker and the speed test on the same page. The files you unloaded should be gone, not just marked non-blocking.
Some files will stay. A theme's main stylesheet is always partly unused on any one page, because it holds the rules for other templates. Aim for a short list of small files, not an empty list.
Common mistakes
- Starting with "Remove unused CSS" before unloading plugin files. It generates per-page CSS from everything that loads, including files you could have removed.
- Delaying all JavaScript on day one. Forms, carts and consent banners stop working until the visitor scrolls.
- Dequeuing by guesswork. Use the handle from the
idattribute; a wrong handle does nothing. - Dequeuing in the parent theme. The next update undoes it. Use a child theme or an mu-plugin.
- Testing while logged in. Most cache plugins do not optimise pages for administrators.
- Keeping plugins you no longer use. Deactivating a plugin removes every file it adds, which beats any optimisation setting.
Questions people ask
How do I remove unused CSS in WordPress without a plugin?
Find the handles of the stylesheets a page does not need from their id attributes, then call wp_dequeue_style() with conditional tags such as is_page() or is_front_page() in a child theme or must-use plugin, on wp_enqueue_scripts at priority 100. Classic themes can also load core block styles per block with the should_load_separate_core_block_assets filter.
Why does the speed test still say "reduce unused CSS" after I installed a cache plugin?
Because most cache plugin settings change how CSS is delivered, not how much of it there is. Loading CSS asynchronously and minifying keep every rule. Only unloading files on pages that do not use them, your builder's per-widget CSS settings, or a "remove unused CSS" feature actually shrink it. Start with unloading, then check the result with the unused code checker.
Which WordPress plugins cause the most render-blocking resources?
Usually page builders, slider and gallery plugins, form plugins, WooCommerce on non-shop pages, and social sharing or popup plugins, because they load their CSS and JavaScript on every page. The exact list differs per site, so measure: the file URLs in the speed test and the unused code checker name the plugin folder each file comes from.