WordPress Plugins That Are Silently Killing Your Site Speed

Some of the most popular WordPress plugins cause serious performance problems that don't show up until a client complains or rankings drop. Here's what to look for and what to do about it.

Cover Image for WordPress Plugins That Are Silently Killing Your Site Speed

If you manage WordPress sites for clients, you've probably had this moment: the site has been live for months, the client hasn't complained, and then someone runs PageSpeed Insights for the first time and the mobile score comes back at 34. The site wasn't fast before either. Nobody just never checked.

Most of the time, the culprit is a plugin. Not necessarily a poorly built one. Usually something installed for a legitimate reason that loads more than it needs to, on more pages than it needs to, and nobody has looked at what it's actually doing since it was set up.

These are the ones worth checking first.

WooCommerce: asset loading and cart fragments

WooCommerce itself is not the problem. The problem is that it loads its scripts and stylesheets on every page of the site by default, including pages that have nothing to do with the store.

A visitor landing on a blog post or an About page is still downloading cart-fragments.min.js, WooCommerce's session management code, and its full CSS bundle. On a site where the store covers a fraction of the total pages, the rest of the site carries that overhead on every single load for no reason.

You can fix this with a snippet in functions.php that dequeues WooCommerce assets on non-store pages:

add_action( 'wp_enqueue_scripts', 'dequeue_woocommerce_on_non_store', 99 );
function dequeue_woocommerce_on_non_store() {
    if ( function_exists( 'is_woocommerce' ) ) {
        if ( ! is_woocommerce() && ! is_cart() && ! is_checkout() ) {
            wp_dequeue_style( 'woocommerce-general' );
            wp_dequeue_style( 'woocommerce-layout' );
            wp_dequeue_style( 'woocommerce-smallscreen' );
            wp_dequeue_script( 'woocommerce' );
            wp_dequeue_script( 'wc-cart-fragments' );
        }
    }
}

The wc-cart-fragments script deserves specific attention. Its job is to keep the cart widget updated in real time without a page refresh. To do that, it fires an AJAX request to /?wc-ajax=get_refreshed_fragments on every page load, for every visitor, whether they have anything in their cart or not. Performance tools like GTmetrix and Pingdom frequently flag this request as a slowdown because on slower hosts it can take two to three seconds to resolve, and it blocks nothing on the page while doing so, but it still adds to total load time and server load.

If your client's theme doesn't display a live cart widget, or if they redirect customers to the cart page on add-to-cart rather than using AJAX updates, this script is doing nothing useful. Disabling it is safe in that scenario. If the live cart widget is needed, Perfmatters has a smarter approach: it checks for the woocommerce_cart_hash cookie and only loads cart fragments when the cart is actually not empty, which preserves the functionality while eliminating the request for the majority of visitors who are browsing without anything in their cart.

The second WooCommerce issue worth auditing is the database. WooCommerce accumulates expired transients, session records, and order data in the WordPress database over time. On a store that has been running for a year or two without cleanup, the autoloaded options table can reach several megabytes. WordPress loads all autoloaded data into memory on every page request before rendering anything. Reducing that table from 2-3MB to under 1MB has shown TTFB improvements of 25-30% on mid-sized stores. WP-Optimize and Advanced Database Cleaner both handle this cleanup.

Elementor: background images and the LCP problem

Elementor is the page builder most client sites are running, and it has two specific failure modes that hurt Core Web Vitals.

The first is how Elementor handles hero images set as CSS backgrounds. This is the default way most Elementor hero sections are built: a container with a background image applied through the Style tab. The problem is that CSS background images cannot receive fetchpriority="high" or loading="eager" attributes. The browser discovers them later in the rendering process than foreground <img> elements, which delays the LCP paint. In testing documented by corewebvitals.io, converting an Elementor background image to a foreground image with the correct attributes dropped LCP from 2.6 seconds to 0.6 seconds on one site.

Elementor does have an "Optimized Image Loading" experimental feature under Settings > Performance that applies fetchpriority="high" to LCP images automatically. The issue is that it sometimes misidentifies which image is the LCP element, and in some documented cases applies both fetchpriority="high" and loading="lazy" to the same hero image simultaneously, which tells the browser to prioritize loading the image while also deferring it. Enabling this feature is worth doing, but verify the output in DevTools afterward.

For hero images that are genuinely the LCP element, the most reliable fix is to add fetchpriority="high" manually via a code snippet targeting a custom CSS class applied to the image widget. This is more work but gives you precise control.

The second Elementor issue is addon plugins. Essential Addons, Ultimate Addons, JetElements, and similar packages each register their own scripts and stylesheets. By default, most of them load those assets on every page regardless of whether any widgets from that addon appear on the page. A simple contact page ends up loading JavaScript for countdown timers, pricing tables, and interactive maps it will never use. Check whether your addon plugins have per-element asset loading options and enable them if they do.

Jetpack: module bloat on every page

Jetpack is installed on a significant portion of WordPress sites, usually because a developer needed one specific feature at some point. The performance issue is that Jetpack is a large suite of loosely related tools and its assets load sitewide regardless of which modules are actually active.

The two modules that create the most visible performance impact are Related Posts and social sharing. Related Posts makes an external API call to Automattic's servers to retrieve content suggestions. That request happens on page load and adds a network dependency to your critical path even though the related posts appear below the fold where they don't affect LCP. Social sharing adds render-blocking JavaScript that serves no purpose until a visitor decides to share something.

Neither of these is difficult to fix. Under Jetpack > Settings, individual modules can be toggled. Disable any module that isn't being actively used and verify afterward. If the only reason Jetpack is on a site is a contact form that was set up years ago, replacing it with Contact Form 7 or WPForms Lite and removing Jetpack entirely is a straightforward decision.

The harder conversation is with clients who are using Jetpack's CDN (Photon) for image delivery. Removing Jetpack in that case requires setting up a replacement CDN before pulling the plugin. Worth doing, but requires a plan.

WP Rocket: correct tool, wrong configuration

WP Rocket is a well-built plugin. The performance problems that come from it are almost always configuration problems, not tool problems.

The most common one: enabling Delay JavaScript Execution globally. WP Rocket's delay feature defers all JavaScript until the user interacts with the page through a click, scroll, or keypress. For static pages with no interactive elements this works well. For pages with chat widgets, analytics that need to fire on load, or forms that initialize on page ready, blanket delay breaks things. The symptom is usually a site that scores well in PageSpeed Insights lab data but has user complaints about elements not working or appearing slowly. WP Rocket provides an exclusion list for this reason. Use it.

The second configuration issue is caching behavior on WooCommerce sites. WP Rocket automatically excludes cart and checkout from caching, which is correct. What it doesn't handle automatically is logged-in users. Once a customer logs in or adds something to their cart, WooCommerce sets session cookies and WP Rocket stops serving cached pages to that user. On a membership site or a store with a high proportion of returning logged-in customers, a large share of traffic is bypassing the cache entirely. The performance gain you measured during setup may not reflect what real users are experiencing.

Test WP Rocket configuration by walking through actual user flows: browse as an anonymous visitor, add to cart, log in, browse as a logged-in customer. Check response headers in DevTools to confirm which pages are being served from cache and which are not.

The thing every fix has in common

None of these are exotic problems. They're default behaviors of popular plugins on typical sites. The reason they persist is that making a change and confirming the change actually worked are two separate steps, and most developers only do the first one.

You disable cart fragments, clear the cache, and check PageSpeed Insights. But PSI has its own caching layer, CDN propagation takes time, and sometimes fixing one bottleneck reveals a second one that was waiting behind it. A score that didn't improve doesn't necessarily mean the fix didn't work, and a score that improved doesn't mean the underlying issue is resolved.

That verification step is what we built into Evalta AI. After you make a change, Evalta re-scans the specific page, checks whether the issue that was flagged has actually resolved, and tells you what's still in the way if it hasn't. For developers managing several client sites, the difference between "I made the change" and "I know the change worked" is where the real time goes.