How to safely remove unused CSS in WordPress

In this post you will learn how to find and remove unused CSS on your website and prevent WordPress from transferring megabytes of unused code without any plugins.

Why should you remove unused CSS in the first place?

Come on, let’s be serious, you know why you should do it. 

You wouldn’t be here if you didn’t want to optimize your web page performance. You’ve probably run your website through a PageSpeed checker and received a suggestion to remove unused CSS.

Many websites use lots of 3rd party libraries or some pre-built themes, with lots of features and code to cover all possible scenarios, while only a small portion of it is really used. You end up transferring megabytes of unused CSS (dead code), which makes your website take really really long to load. 

How to find unused CSS?

If you use WordPress, unfortunately there isn’t an easy way to find unused CSS, like by just installing a plugin and clicking through some configuration steps. Although there are some tools online, that help to find the needed CSS by submitting your URL. 

Purifycss.online, for example, crawls your web page and checks all CSS files and inline styles as well. But caution: it will remove all CSS rules that aren’t used on the specified page, even though some of them might be used on sub pages. You have the possibility to insert multiple URLs, but if you manage a site with hundreds of pages, it might get quite cumbersome.

purifycss.online with multiple URLs and results

Disclaimer: purifycss.online is made by me. The JavaScript library purifycss, which the online tool is based on, is not.

How to use the purified CSS code on your WordPress website

1. Upload purified stylesheet

Now, if you already have your clean CSS code, let’s copy & paste it in a new file and name it styles.pure.css and upload it to your theme folder (if you’re using a pre-built theme, I hope you already created a child theme).

If you want to have separate CSS files for each page type, you could name your files styles.front-page.pure.css, styles.pages.pure.css, styles.blog.pure.css, styles.archives.pure.css etc.

2. Remove existing stylesheets

Now you’ll have to remove all stylesheet references from your web page. You can do this by iterating through all queued styles and dequeue them. Add this to your functions.php:

// set a variable to make it easy to enable and disable the purifycss feature
// while testing, just append this parameter to your url: http://www.yourwebsite.com/?purifytest
$purifyCssEnabled = array_key_exists('purifytest',$_GET);

// when you're done, set the variable to true - you will be able to disable it anytime with just one change
// $purifyCssEnabled = true;

function dequeue_all_styles() {
    global $wp_styles;
    foreach( $wp_styles->queue as $style ) {
        wp_dequeue_style($wp_styles->registered[$style]->handle);
    }
}

if ($purifyCssEnabled) {
    // this will remove all enqueued styles in head
    add_action('wp_print_styles', 'dequeue_all_styles', PHP_INT_MAX - 1);

    // if there are any plugins that print styles in body (like Elementor),
    // you'll need to remove them as well
    add_action('elementor/frontend/after_enqueue_styles', 'dequeue_all_styles',PHP_INT_MAX);
}

3. Make sure all styles have been removed

Now open your web page URL with the ?purifytest parameter appended and you should see only the raw content your page. Open the source code viewer in your browser and make sure there is no <link rel="stylesheet" ...> and no <style>...</style> block left.

4. Remove inline styles if any exists

Unfortunately there is no WordPress hook to remove inline styles, as they are not enqueued like the stylesheet file references. 

The only possibility that comes to my mind (please let me know in the comments if there is a better way), is to go through the HTML that is about to be rendered and remove inline styles with regular expressions. To catch the full HTML, we need to do a workaround with output buffer.

Add this to your functions.php file:

/* Remove inline <style> blocks. */
function start_html_buffer() {
    // buffer output html
    ob_start();
}
function end_html_buffer() {
    // get buffered HTML
    $wpHTML = ob_get_clean();

    // remove <style> blocks using regular expression
    $wpHTML = preg_replace("/<style[^>]*>[^<]*<\/style>/m",'', $wpHTML);

    echo $wpHTML;
}
if ($purifyCssEnabled) {
    add_action('template_redirect', 'start_html_buffer', 0); // wp hook just before the template is loaded
    add_action('wp_footer', 'end_html_buffer', PHP_INT_MAX); // wp hook after wp_footer()
}

5. Enqueue the purified CSS

function enqueue_pure_styles() {
    wp_enqueue_style('pure-styles', get_stylesheet_directory_uri().'/styles.pure.css');
}

if ($purifyCssEnabled) {
    // enqueue our purified css file
    add_action('wp_print_styles', 'enqueue_pure_styles', PHP_INT_MAX);
}

If you chose to have separate CSS files for each page type, you can use built-in conditions to set the filename:

function enqueue_pure_styles() {
    $suffix = '';
    if (is_front_page()) {
       $suffix = '.front-page';
    } else if (is_page()) {
       $suffix = '.pages';
    } else if (is_single()) {
       $suffix = '.blog';
    } else if (is_archive()) {
       $suffix = '.archives';
    }
    wp_enqueue_style('pure-styles', get_stylesheet_directory_uri().'/styles'.$suffix.'.pure.css');
}

6. Test your changes thoroughly!

Please keep in mind that you might have thrown away CSS code that is being used on sub pages or that is dependent on some user interaction, like a modal dialog, form validation messages, AJAX callbacks etc. 

Some tips what to pay attention to:

  • Hover over and click on each element
  • Try submitting your forms with incomplete or invalid data
  • Also test pages that are not linked from your home page or through your main navigation
  • Test on different screen sizes – not just by resizing your window, but also on different physical devices
  • Test in different browsers!
  • Please comment below with other frequent cases that might be missed by purifycss.online

7. Adjust purified CSS code

If you detect styles that are missing in your purified code, make sure first, that you added the URL of the page to the “additional URLs” on purifycss.online.

If that’s the case, then copy the corresponding HTML code of your unstyled element (like a modal popup) and add it to “custom HTML code”. 

For this, right click on the element, click “Inspect Element”, then in the Element view, right click on the tag and click Copy > Copy outerHTML

Copy element as HTML in Chrome Developer Tools

Then in purifycss.online paste the code into “Add custom HTML code”.

Add custom HTML code in purifycss.online

And then repeat from step 1 😉

Bonus tip: Optimize for First Contentful Paint (< 2s)

Now check your PageSpeed score again. You’ve done a really great job so far!

But there is one more thing you can optimize. It’s not just the PageSpeed score that matters, the perceived speed of your site matters much more. If your site takes longer than 2 seconds to show the first meaningful content, your users will be annoyed and might leave before your page even starts to render.

Even if the unused CSS is removed, your styles might still be too large. While the browser loads the CSS file, your web page remains blank. On slow connections (mobile 3G), this could be longer than we would like. 

Wouldn’t it be much better if the browser could load the CSS that is necessary to show the first visible portion right away? The rest could be loaded after the most important content is already visible. This technique is called Critical Path CSS. You’ll basically include the styles needed for the above-the-fold content as an inline-style and load the rest asynchronously. Read more about how to find critical CSS in detail.

Need a No-Code-Solution for PurifyCSS as WordPress Plugin?

Do you feel overwhelmed or unconfident with messing around in your PHP code?
I agree with you: the simplest way would be to run this purification process through a WordPress plugin. Unfortunately, there isn’t a PurifyCSS WordPress Plugin yet, but it’s on my plan.

Oh, no! I just noticed that I’m running out of coffee. ? I desperately need some coffee before being able to continue coding!
If you need this feature, please go to purifycss.online and buy me a coffee by clicking BuyMeACoffee in the top right corner. Thanks
😉

72 thoughts on “How to safely remove unused CSS in WordPress”

  1. hello,
    thank you for this tutorial,
    i have wordpress bundled with wpforo forum with di theme, i tried your tuto, but i have an issue with wpforo css and tinymce, can’t force them like elementor
    any suggestion?
    thank you

    1. Hi, the CSS calls you’re seeing in your DevTools Network tab, are initiated by tinymce.min.js. I don’t think you can do much about that, but it doesn’t necessarily harm your performance, as they are loaded asynchronously after page load. What I would suggest instead (not sure if that’s possible) is to try disabling tinymce for the pages where the editor is not needed, e.g. homepage.

    1. Hi Kerem, I can’t think of any conflict with Elementor. In my tests it worked just fine.
      Make sure to have backups before making any permanent changes and let me know if you get any conflicts.

  2. hello I have uploaded in the theme folder the styles file generated via the cpanel and then I inserted in the functions.php those codes you wrote in this guide, but how do I know if everything went well? is there a way to verify that the website is actually using the purified css? And regarding the other guide on critical css, I pasted the css before the / head tag in header.php but I don’t understand step 1.2, what should be done? what needs to be added in the body tag? can you give an example?

    Thanks

    1. Hi Charles, the easiest way to test if the purified CSS is used, is by opening the Developer Tools (F12 in Chrome) “Network” tab and filter by “style” or “css”. After refreshing the page, you will see which files were loaded.

      For critical CSS, you’ll need to insert the generated critical CSS code between <style> … </style> tags in <head> part of the page, while the reference to the rest of CSS, e.g. <link rel=”path/to/style.css”> must go in the footer.php.

    2. “you’ll need to insert the generated critical CSS code between <style> … </style> tags in <head> part of the page” in which pages?

      “while the reference to the rest of CSS” how are references made? is there a code to write? if you could make me understand how to write it? an example of code.

      thanks

    1. I am planning to build one, but didn’t make any significant progress yet. Subscribe to my newsletter, if you want to get such updates.

    1. Hi Moses, there is no plugin-based solution yet, as far as I know. But I’m seeing high demand here and plan to build a plugin. Sign up to the newsletter and get informed, when it’s ready.

  3. I don’t understand step two. So I get to skip current stylesheets, but how would I go with the clean up? Can you direct me where can I remove these stylesheets which are loading up and have a ton of code not used?

    I ran web-domain.com/?purifytest and it loaded the page without styles, what is the next steps to follow before getting to step 3 and right after 3.
    It seems its just temporary if i’m running ?purifytest.

    Awesome solution Peter.

    1. Hi Eduardo, step 1 assumes that you already have a purified CSS code. If you don’t have it yet, then go to purifycss.online, enter your URL(s), download the result file, name it styles.pure.css and upload it to your theme folder. Let me know if this helps. Cheers

  4. Hello Peter!

    Thank you so much for this solution! I think I have done all your steps correctly but want to be clear on this 1 step before implementing.

    In step 2 you say,

    // set a variable to make it easy to enable and disable the purifycss feature
    // while testing, just append this parameter to your url: http://www.yourwebsite.com/?purifytest
    $purifyCssEnabled = array_key_exists(‘purifytest’,$_GET);

    // when you’re done, set the variable to true – you will be able to disable it anytime with just one change
    // $purifyCssEnabled = true;

    where exactly do I set the variable to ‘true’?

    do I change the parameter
    $purifyCssEnabled = array_key_exists(‘purifytest’,$_GET);
    to
    $purifyCssEnabled = true;
    ?

    Or do I leave that parameter as is and add
    $purifyCssEnabled = true;
    to my functions.php before or after one of the pieces of code you provided?

    Thank you in advance for your help!

    -Kismet

    1. Thank you, Kismet for your feedback!

      I would suggest to comment out the line with
      // $purifyCssEnabled = array_key_exists(‘purifytest’,$_GET);
      and set the variable to true right below.
      $purifyCssEnabled = true;

      With that you can easily switch back and forth between the two modes by commenting/uncommenting the two lines when needed.

  5. Hi ‘Peter, thanks for great article, I have one slight problem.

    I’m using a plugin to generate cookie consent.

    When I enable remove inline css blocks. (it strips out the popover on first view to consent to cookies) I commented out those lines from your functions insert and its back… (is there any way to exclude that from happening) so I can take advantage of the strip inline css blocks AND retain the functionality of this.
    (website linked in my comment)
    regards Lee

  6. This is so DESPERATELY needed as a wordpress plugin. If it also works with Autoptimize plugin that would be amazing. Following for sure!!

  7. Peter,

    Really great and interesting solution. I’d like to give it a try, but I’m curious- Does this method prevent me from making any additional css changes from within wordpress (I use X Theme > Cornerstone Editor). If this is the case, if I make changes to design/site, would I have to toggle back so all styles are queued again, go through purifycss, and update the styles.pure.css?

    1. Hi Matt, I don’t know the X Theme, but it’s very likely, that you’ll need to disable the purifycss solution (e.g. by setting $purifyCssEnabled = false;) while working on changes and go through the whole purifycss process again when you’re done. I know that’s still some manual work, but hopefully I can provide an at least semi-automated solution soon.

  8. ​Hi, I have done CSS file to purify the CSS of my website. Now the problem is that the elegant font and font awesome font are not being downloaded. It is now showing up 404 on the console. Also the top menu has moved to a small part at the bottom of the footer. Could you please help me to solve the issue? here is my website: https://kellersstore. com

  9. Hi Peter,
    thanks for the great tool and post, following the two first steps and trying to remove all existing css I still got theme css coming. Any ideas?
    Thanks,
    Giorgos

  10. Hi Peter, thanks for your help before.

    I implemented purify css and it works a treat, however it seems to conflic with Autoptimize, even if the settings for css optimisation are left unticked, it breaks the other functions, of Autoptimize.

    Is there anyway to ensure the functions fire before Autoptimise, so that Autotimize is applied to the purified file? that way it would be possible to ensure the other elements of Autoptimze still work.

    Cheers, Lee

  11. Hello,

    Thanks for this!

    When I follow those steps and do the test I only get a blank page (I’m using Elementor).

    I went to https://purifycss.online/, entered my site, with “Minify Output” selected I clicked on “download combined, purified & minified CSS”. With that file I uploaded it to my child theme folder (themes/child-theme) and main theme folder (themes/twentytwenty/). Began adding code to functions.php in my child theme. After step 3 I could see the the text layout without any CSS but as soon as I implement the code from Step 4 the site starts showing a blank page. If I skip step 4 and implement Step 5 then the site show some content but most of it is missing the styling like background colors, forms colors, etc.

    Any advice for this? I’m using Elementor Pro to build the pages.

    Thanks!

    1. Hi Sam,
      I also noticed, that purifycss doesn’t work well with some Elementor pages. There was a bug with the handling of pseudo-classes like div:not(.foo), which is used very frequently in Elementor. This is fixed now, so you might try it again.
      Also, Elementor uses different stylesheets for each page that might not be compatible with each other – so be careful there.

  12. Amazing tool! It works great. But it removes the styles for the admin bar on the front-end. How can I keep those styles? Thank you.

    1. Thank you, Dan!
      At step #2 you can skip admin-bar stlyes from being removed:

      foreach( $wp_styles->queue as $style ) {
          if ( $style=='admin-bar' ){ continue; }
          wp_dequeue_style($wp_styles->registered[$style]->handle);
      }
      
  13. Hi, thank you for your brief solution. I’m using WordPress and have lot of the same issues, and I don’t like to install more plugins. I already installed 14 required by the theme. Once I used a plugin, after using that plugin I got the worst result. All the images went blurry, black and mix up the site content even header and footer. I console the site and see the same result as you mentioned above after purifying the CSS. All website source code went wrong and I changed back it to normal and started searching another way to do it without any plugins and errors. I’m still in problem, so please suggest an easy method.

    Thanks
    Haider

    1. I haven’t yet. Have you tried it?
      I’m actually looking people who can test the plugin with different themes & setup in private beta. Please send me an email if you’re interested.

  14. Pingback: How I Speeded Up This WordPress Site - Open Source Software Freelancer

  15. In our case, we have many modules that aren’t used on our home page. Other pages use them all. We can strip out the CSS for those pages, but – then we have to have many different .css files and logic – and added HTTP requests for those files. How would your WordPress plugin know how to deal with those scenarios?

    1. That is indeed an excellent question, a problem I am facing right now and not yet sure how to solve it.
      One possibility would be to include one common CSS for all pages, which would take pretty long to generate. In that case you’d need to make sure, that one page’s styles doesn’t conflict with other page’s styles – I had one case today, where this didn’t work.
      Another, and maybe the better approach would be to generate one CSS file for each page. But that would undermine caching gains.
      If you have any idea, I’m open for discussions 🙂

  16. Peter,

    This is no doubt the hottest topic in WordPress optimization right now, especially with Google’s Web Vitals update to PageSpeed Insights and the other Google optimization tools.

    Now it looks like I am going to have to refund a client out of shame – my magic tricks are no longer magical. I’m sinking in a sea of red and orange, not surfing on waves of maqnificent green.

    The sooner your plugin hits the street, the quicker I can feel confident about my speed skills again. 🙂

    Good luck with your plugin – it’s a certain winner.

  17. I believe a plugin for this function is necessary since themes and plugins which contribute to the unused CSS are updated with features quite often and it seems to me when they are updated this process has to be repeated to clean the new CSS. That is if someone wants to take advantage of the new features or new updates. I believe your plugin, when you are done with it, will definitely be in high demand.

  18. Hello Peter,

    This is exactly what I was looking for. As a not so savy with code kinda guy I coulld still manage to implement your tutorial.
    There is only one thing that would make it even more perfect. At least I like to think so. And that is to print out the styles.pure.css in the footer. This way it does not pop up in de page speed check as renderblocking. The only problem is my php knowledge is to little for this. I tried somethings but nothing worked. Do you have an idea?
    It really would be the icing on the cake.

    Thanks, Koen

  19. Hi,

    Thank you very much for your contribution,

    I followed the steps, the dequeuing is working, but the enqueuing of the purified stylesheet is not working 🙂

    Not sure if it got to do with the function used. I uploaded the file in the theme folder.

  20. There was some problems encountered though, it doesn’t work well with sliders. It removed them in some case, or it merged with the elements that are below.

  21. Edit: The custom HTML fixed the slider problem. However, no noticeable improvement in google’s pagespeed score.

  22. All in all. I think this is not needed. The AMP plugin does more or less the same thing, it perform a CSS tree-shaking with few design change. The CSS removal is a hard task, there website where it completely missed elements and changed the entire page.

    The only case you will need this, is when you really don’t want any design change for your website.

  23. Hey there,
    Love the tutorial, it’s really easy to follow.
    I face this one problem. After I activate the code, the search and menu hamburger icons disappear, only the squares are visible in their place. I tried to manually put HTML codes for them into the purifycss, but they still don’t show. Do you have any idea why this could happen?
    Thanks.

  24. It’s worked…! nailed down unused CSS. Some icon fonts (i.e. Font Awesome, Simple line icons) required path correction. As CSS belongs to these fonts were loaded from theme path & the derived CSS uses theme (child) path. I have used OceanWP. Anyway, Peter Bagi sir did exceptional job. Thank you.

  25. Hi there,

    I’m trying this out on my WP site but I’m getting an error..

    undefined (at undefined:undefined): “undefined” while purifying (followed by url)…..

    Where am I going wrong? Is it related to the version number that follows my css file?

    Thanks,

    Brian

    1. It turns out that this kind of error message happened quite often, when a URL couldn’t be fetched by the API. I corrected the backend, so that it won’t fail completely if only one stylesheet URL is unreachable or not found. I also adjusted the error messages, so that they don’t sound that weird 🙂

  26. This tool is amazing enough. It help up me very much on many of my web development projects. On the future, please add support for AMP HTML so I can minify up my AMP websites too. But this is only my suggestion, so don’t take up it into your mind.

  27. Great job, I haven’t fully tested it yet, but I’m trying on.
    It would be useful in the WordPress context to generate separate .css file for separate is_front_page, is_pages, is_singular types. For example, when adding the url link asdf.com/category – select is_category and a file is generated, later in functions.php, including the is_category condition.

  28. and does the script also check jquery / javascript files or only HTML because we can remove classes from css files and they can be used in .js files, am I right?

  29. I really want to use this with Divi sites, but I find the lack of a plugin to be a problem in creating a dependency on constantly updating the purified CSS via PurifyCSS website interactions. Just putting in my own +1 for there to be a PurifyCSS plugin at some point. Thanks!

  30. Hi Peter, thank you very much for this tutorial.
    I followed all steps. Now I see that Icons are not displayed and that some background-images are missing. How can I make it work?
    Thank you!

  31. Came across this awesome tool to try and prune lots of css from a packaged theme (html, non-wordpress). Some thoughts:
    (a) it would be awesome if there was a way to run purify for multiple screen formats at once and generated a combined file – the tool did not pick up many from responsive.css
    (b) I came across a css style that was (understandably) undetected:
    li:before{content:”\f00c”;position:absolute;top:1px;left:0;font-family:Font Awesome\ 5 Free;font-weight:900}
    (c) it would be nice to have a semi-minified version that had clear file-breaks in the purified result, making it easier to hand-fix afterwards.
    (d) multiple @charset “UTF-8”; get introduced throughout. Not a deal breaker.

    excellent stuff, thanks!

  32. this post is a game-changer for my blog’s speed I’m the kind of person who prefers the appearance of the website over the performance and because of that my blogs load very slow and even after trying many methods and installing several cache plugins I still can’t able to fix that issue but when I read this post very carefully and follow instructions now I learned how I can remove the unused CSS from my blog post and the speed of my blogs increased dramatically. thank you so much for sharing such an effective method <3

  33. hello! I liked it! Thank you very much.
    I would just like to improve the code generating uniques “styles.pure.css” and calling for specific page types by taking advantage of wordpress conditionals tags (is_single, is_page, is_front_page etc). That way, I believe that each type of page would call an even smaller css. For example, many sites use Elementor only on pages and homepage, but not on custom taxonomy, single posts, tags, categorys, etc.

    1. This is a fantastic suggestion! Thank you for that!
      With unique styles, do you mean to have one CSS for each page type? That shouldn’t be a problem, and I updated the post.
      Or do you mean to have a unique hash in the filename, so that they bypass caches when changed? I’m not sure how to automatically generate these filenames because the files are uploaded manually. You would have to change the filename and adjust the code accordingly.

    2. Hi Peter. Thank you very much for the code snippet. It really is useful. But it’s not exactly what I proposed. English is a little difficult for me.
      I’ll try to explain it more practically.
      My website has pages that are extremely important for SEO (with Core Web Vitals, they were severely hampered because of the performance score) and others that are not indexed. So, because of the difficulty in manually generating the files whenever my landing pages are modified (I use Elementor to create the pages), it is not necessary for Purifycss to run the entire site. I just want to index the homepage, blog (post) and my custom taxonomy pages well. So:

      1) I want to use Purify CSS only on the homepage (is_front_page), blog ( is_single(‘post’) ) and this custom taxonomy ( is_tax( ‘stores’ ) )
      2) as this is something solely to Google’s satisfaction, this css file replacement also is not necessary for logged in users. So it would be interesting not to run if is_user_logged_in()

      I’m pretty sure it’s possible, but as I’m not a programmer, I don’t know exactly how to do it. I appreciate it very much if you can help me

Leave a Comment

Your email address will not be published. Required fields are marked *