HTML Minification and Critical CSS Inlining in WordPress

Removing whitespace from HTML output and inlining above-the-fold CSS are two of the highest-ROI performance techniques for WordPress — they reduce Time to First Byte payload and eliminate render-blocking stylesheets in a single request.

Problem: WordPress loads full stylesheets on every page even when only a small portion of CSS is needed for the initial viewport — delaying the First Contentful Paint and causing layout shifts.

Solution: Extract critical CSS for above-the-fold content with the critical npm package, inline it in <style> tags via wp_add_inline_style(), and load the full stylesheet asynchronously with rel="preload" + onload. Automate regeneration in your build pipeline after any CSS change.

The examples below implement an output-buffer HTML minifier as a WordPress plugin, and show how to extract and inline critical CSS while deferring the full stylesheet.

// Minimal HTML minifier via output buffering
// Place in mu-plugins/html-minify.php

add_action( 'template_redirect', function() {
    if ( is_admin() || wp_doing_ajax() || is_feed() ) return;
    ob_start( 'myplugin_minify_html' );
} );

function myplugin_minify_html( string $html ): string {
    // Skip if the page contains a <pre> or <textarea> (preserve formatting)
    if ( preg_match( '/<(pre|textarea)[^>]*>/i', $html ) ) return $html;

    $search = [
        '/\>[^\S ]+/s',      // strip whitespace after tags
        '/[^\S ]+\</s',      // strip whitespace before tags
        '/(\s)+/s',          // collapse multiple spaces to one
        '/<!--(?!\[if).*?-->/s', // remove HTML comments (keep IE conditionals)
    ];
    $replace = [ '>', '<', '\1', '' ];
    return preg_replace( $search, $replace, $html );
}

Defer non-critical stylesheets and inline critical CSS:

// 1. Inline critical CSS in <head>
add_action( 'wp_head', function() {
    $critical_css_file = get_template_directory() . '/assets/critical.css';
    if ( file_exists( $critical_css_file ) ) {
        echo '<style id="critical-css">' . file_get_contents( $critical_css_file ) . '</style>';
    }
}, 1 );

// 2. Convert render-blocking stylesheets to deferred loading
add_filter( 'style_loader_tag', function( $tag, $handle, $href, $media ) {
    $defer_handles = [ 'my-theme-style', 'woocommerce-general' ];
    if ( ! in_array( $handle, $defer_handles, true ) ) return $tag;

    // Load asynchronously, swap media to 'all' on load
    return sprintf(
        '<link rel="preload" as="style" href="%s" onload="this.onload=null;this.rel='stylesheet'">' .
        '<noscript><link rel="stylesheet" href="%s"></noscript>',
        esc_url( $href ),
        esc_url( $href )
    );
}, 10, 4 );

// 3. Add dns-prefetch / preconnect for external resources
add_action( 'wp_head', function() {
    echo '<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>';
    echo '<link rel="dns-prefetch" href="//fonts.gstatic.com">';
}, 0 );

NOTE: Generate the critical.css file with Critical (Node.js) or the online Critical CSS Generator, then automate regeneration in your CI pipeline whenever theme CSS changes.

Leave Comment

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