Skip to content
WP speed & Core Web Vitals

WordPress Speed Audit: The 25-Point Checklist We Use on Client Sites

The full 25-point WordPress speed audit framework WitsCode runs on paid engagements, with the why, how-to-test, and fix path for every item.

By WitsCode13 min read

A proper WordPress speed audit is not a PageSpeed Insights screenshot with red numbers circled. It is a structured walk through twenty-five named failure points, in a specific order, each with a test that produces an unambiguous pass or fail and a fix path that someone in the room can actually execute. The five items that recover the most performance on the average WordPress site, in our experience across roughly 200 audits at WitsCode, are server time to first byte at the origin, the autoload size of the wp_options table, the global JavaScript footprint of the active page builder, the loading strategy for the LCP hero image, and the cache hit ratio at the CDN edge. If a site fails on those five it will never reach a passing Core Web Vitals grade no matter what plugin a forum thread recommends, and a competent audit names them in the first hour.

This article is the full framework, free to use, in the same order our team applies it. Each of the twenty-five items below is a paragraph rather than a checkbox because a checkbox tells you what to look for but not why a passing score is worth chasing or what to do when it fails. We charge clients for the engagement, not the framework. The framework is below in full. If at the end you would rather hand it to someone who has run it 200 times, the closing section explains how the WitsCode 25-point audit engagement works.

Server and infrastructure

The first audit pillar is everything that happens before WordPress sends a single byte to the browser, and it begins with time to first byte at the origin. We test TTFB by running a WebPageTest session from a cold cache against an uncached URL, typically a logged-in admin preview or a query string-cache buster, and we expect the first byte to arrive in under 600 milliseconds for an uncached request and under 200 milliseconds for a cached one. Anything beyond those numbers points to one of three causes that we will check in turn. The fix path on shared hosting is almost always migration to a managed host that runs PHP-FPM with persistent OPcache and a full-page cache layer, which on the providers we vet means Kinsta, WP Engine, Rocket.net, or Cloudways with the Pro stack. We do not recommend tuning a slow shared host. The math never works.

The second item is PHP version and worker count, which sits inside the host but exposes itself through Site Health and through how quickly cached pages regenerate after an editor saves a post. PHP 8.2 or 8.3 produces between 30 and 50 percent better request throughput than PHP 7.4 on identical code, and the upgrade is usually a one-click toggle in the host control panel that breaks nothing on a maintained site. Worker count matters more once the site is past about 50,000 monthly visits. The fix path is to upgrade to PHP 8.2 minimum, run the Plugin Performance Profiler or Query Monitor to confirm no plugin is throwing fatal errors on the new version, and ask the host to scale workers to match peak hour traffic.

Third is the object cache, by which we mean a persistent Redis or Memcached layer that survives across requests and stops WordPress re-querying the database for the same wp_options row hundreds of times per page load. We test for it by inspecting the response headers for an X-Cache-Object marker and by running the Query Monitor plugin to see the database query count, which on a well-cached homepage should sit under 30 queries and on an uncached one under 80. The fix path is the Redis Object Cache plugin by Till Kruss combined with a host that exposes a Redis socket, which is standard on Kinsta and WP Engine and available as an add-on on most managed providers.

Fourth is database health, and specifically the autoload size of the wp_options table. Every WordPress page load runs SELECT option_value FROM wp_options WHERE autoload = 'yes' before it does anything else, and on neglected sites that single query returns five megabytes of expired transients, abandoned plugin settings, and orphaned theme options. We test by running SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload='yes' through Adminer or phpMyAdmin and expect a result under 800 kilobytes. The fix path is Advanced Database Cleaner Pro by Jose Mortellaro, which finds orphaned options and lets you flip individual rows from autoload to no without deleting them.

Fifth is content delivery network coverage and the protocols it speaks. We test by running curl with the --http3 flag against the homepage and a static asset, and we expect a 200 with HTTP/3 negotiated. We also expect Brotli compression on text assets and an immutable cache header on versioned static files. The fix path is Cloudflare with the APO add-on for sites under heavy traffic, or Bunny CDN with Bunny Optimizer for sites where image transformation needs to happen at the edge. Either choice removes 100 to 300 milliseconds of TTFB for users outside the origin region and is the cheapest single performance win on most builds.

Asset pipeline

The sixth audit item is the CSS pipeline, with critical CSS and unused CSS as the two related failures. We test by opening Chrome DevTools, running the Coverage tab on the homepage, and reading the unused bytes percentage on every stylesheet. A passing site uses under 30 percent unused CSS on the largest stylesheet. A failing site, which is most builds running Astra Pro plus Elementor plus a starter template, runs at 75 percent unused. The fix path is WP Rocket's Remove Unused CSS feature, FlyingPress's Lazy Render CSS, or a Perfmatters and Asset CleanUp combination that dequeues per-page stylesheets the page does not use.

Seventh is the JavaScript execution path, where the failure mode is total blocking time during initial load. We test by running Lighthouse in mobile mode and reading the TBT score, which should sit under 200 milliseconds for a passing audit. The fix path on every WordPress build we have ever audited is to enable Delay JS in WP Rocket, Perfmatters, or FlyingPress, which postpones non-critical script execution until first user interaction and typically removes 80 percent of TBT in a single toggle. Sites that ship their own custom JavaScript bundles need a complementary fix that splits the bundle along route boundaries, which is a code change rather than a plugin setting.

Eighth is image format and the responsive srcset attribute. We test by viewing the page source and confirming that hero images carry srcset with multiple width descriptors, that the format is WebP or AVIF rather than JPEG, and that the encoded weight under 1500 pixels wide stays under 150 kilobytes. The fix path is ShortPixel Adaptive Images for sites that want the conversion to happen at upload time, or Bunny Optimizer for sites that want it to happen at the edge. AVIF compresses 20 to 30 percent smaller than WebP at equivalent quality and is now supported across every browser we test, which means new builds should default to AVIF with WebP as fallback.

Ninth is lazy loading and the explicit exception that has to be made for the largest contentful paint image. WordPress 6.3 and later add loading="lazy" automatically to every image, which is correct for everything below the fold and catastrophic for the hero image at the top of the viewport because it delays the LCP. We test by inspecting the LCP element in PSI and confirming it carries fetchpriority="high" and does not carry loading="lazy". The fix path is the Optimize LCP toggle in Perfmatters, manual addition of the fetchpriority attribute through a template filter, or the FlyingPress LCP optimization module which detects the element automatically.

Tenth is the font loading strategy, where flash of invisible text and flash of unstyled text both kill the perceived speed. We test by recording a slow 3G profile in DevTools and watching the rendering filmstrip for any visible delay between background paint and text paint. The fix path is to self-host every Google Font using OMGF by Daan van den Bergh, set font-display: swap on every face, and add a link rel="preload" tag for the single font weight used in the LCP heading. Google Fonts loaded from fonts.googleapis.com fail this audit by default because the third-party DNS lookup adds round trips that no caching layer can remove.

Plugins

The eleventh item is the total plugin footprint, which we measure not by count but by combined frontend asset weight. A site running 40 plugins where 35 of them are admin-only and ship no frontend code passes this audit. A site running 12 plugins where 8 of them load a frontend bundle each fails it. We test by opening DevTools Network tab, filtering by JS and CSS, and grouping requests by /wp-content/plugins/ subdirectory. Anything contributing more than 30 kilobytes compressed to a non-relevant page is flagged. The fix path is Asset CleanUp Pro by Gabe Livan, which lets you dequeue any plugin asset on any URL or URL pattern without touching the plugin code.

Twelfth is the subset of plugins that load admin-only functionality on the public frontend. The repeat offenders here are Wordfence, which injects scan-related scripts on every page load, iThemes Security, which adds CSP-related markup, and any backup plugin that exposes admin-bar nodes to non-admin users. We test by viewing the page source while logged out and grepping for plugin slugs that should not appear. The fix path is the same Asset CleanUp dequeue, applied per-plugin, or a hardened wp_dequeue_script call inside a child theme functions file, gated by an is_admin check inverted with a not.

Thirteenth is heartbeat frequency and admin-ajax traffic. WordPress's heartbeat API polls /wp-admin/admin-ajax.php every 15 seconds on the admin side and every 60 seconds on certain frontend builders, which on a multi-author site creates noticeable server load and on a Lite Speed cached frontend can break the cache layer. We test by opening Network tab on a frontend page, leaving it idle for two minutes, and counting the admin-ajax requests. The fix path is Heartbeat Control by WP Rocket, set to disable on the frontend and to throttle to 60 seconds on the admin and 120 seconds on the editor.

Fourteenth is the security plugin's frontend behavior more generally, because Wordfence in particular ships a 60 kilobyte JavaScript file to every visitor for click fraud detection that most sites do not need. We test by isolating Wordfence in DevTools and reading the contributed bytes. The fix path is to switch to a lighter security model, which on managed hosts means relying on the host's WAF and limit login attempts feature combined with Solid Security Pro on the application layer, or to dequeue the Wordfence frontend module through its own settings panel under Tools, Diagnostics.

Theme and builder

The fifteenth item is the active theme's contribution to the rendered HTML and CSS, which we score against a baseline of GeneratePress or Kadence on a default install. A theme that adds more than 50 kilobytes of CSS over that baseline before any builder is loaded fails the audit. The repeat offenders are Avada, BeTheme, The7, and Bridge, all of which ship a multi-megabyte stylesheet that contains every option the theme has ever supported. The fix path on a new build is to choose GeneratePress, Kadence, or Blocksy. The fix path on a legacy build is harder and usually involves a phased migration that we cost separately.

The sixteenth item is the page builder runtime, which on Elementor adds about 200 kilobytes of JavaScript to every page and on Divi exceeds 600 kilobytes. We test by isolating the builder's directory in DevTools and reading the bundle weight. The fix path on Elementor is to enable every flag under Settings, Experiments labelled "Optimized" and to dequeue specific Elementor assets per page through Asset CleanUp. The fix path on Divi is harder. The fix path on a new build is to use Bricks Builder or GenerateBlocks Pro, both of which ship near-zero runtime JavaScript and pass this audit by default.

The seventeenth item is the SEO plugin's frontend overhead, which on Yoast and Rank Math is mostly schema generation and a small admin bar bundle. We test by isolating those plugin slugs in the Network tab and confirming the schema JSON-LD weighs under 5 kilobytes. The fix path on a heavily customized schema setup is SEOPress or Slim SEO, both of which produce smaller schema output and ship lighter frontend code.

Third-party scripts

The eighteenth item is the tag management layer, which is almost always Google Tag Manager loading a long chain of marketing tags, each of which loads its own dependent scripts. We test by running WebPageTest with the Block Domains option enabled against googletagmanager.com and reading the LCP delta. A site that loses 800 milliseconds of LCP to GTM is failing. The fix path is to move tag management to Cloudflare Zaraz or to a server-side GTM container on a Google Cloud Run instance, both of which remove the third-party DNS lookup and run the tag logic on the edge instead of in the user's browser.

The nineteenth item is the chat widget and exit-intent popup category, which on Intercom, Drift, HubSpot Chat, OptinMonster, and Sumo each contribute between 200 and 400 kilobytes of compressed JavaScript and bind global scroll and mouse listeners that make INP fail on every interaction. We test by isolating the third-party domain in Network and reading the impact. The fix path is to load these scripts on first user interaction rather than on page load, which WP Rocket Delay JS and Perfmatters both support, or to replace the live chat widget with a static button that loads the real script on click.

The twentieth item is the affiliate, retargeting, and analytics pixel collection, which on a typical lead-gen site includes the Meta Pixel, the LinkedIn Insight Tag, the TikTok pixel, the Microsoft Clarity script, and a Hotjar session recorder, all firing on every page load. We test by counting third-party origins in WebPageTest and expect the count to stay under eight on a passing audit. The fix path is consolidation to one analytics tool plus one ad pixel where business goals allow, and Cloudflare Zaraz or Stape's server-side container for everything else.

The twenty-first item is the embedded video, where a single YouTube iframe loads roughly 500 kilobytes of player JavaScript before the user clicks play. We test by counting iframes from youtube.com and vimeo.com on the page. The fix path is the lite-youtube-embed web component by Paul Irish, the WP YouTube Lyte plugin for non-developers, or the Lazy Load for Videos plugin by Kevin Weber, all of which replace the iframe with a thumbnail that loads the full player only on click.

Frontend rendering and Core Web Vitals

The twenty-second item is the largest contentful paint element itself, which on most pages is the hero image or the headline above it. We test by running PSI and reading the LCP value at the 75th percentile against the 2.5 second threshold. The fix path layers four optimizations. The image carries fetchpriority="high" and skips loading="lazy". The image format is AVIF with WebP fallback. The image is preloaded with link rel="preload" if it is set as a CSS background. The hosting CDN serves the image from an edge POP within 50 milliseconds of the user.

The twenty-third item is cumulative layout shift, where the failure modes are unsized images, late-loading webfonts, and dynamically injected ads or cookie banners. We test by running PSI and reading the CLS value against the 0.1 threshold. The fix path is to ensure every image carries explicit width and height attributes, every embed iframe carries an aspect-ratio CSS property, every web font loads with font-display swap and a matched fallback metric override using size-adjust, and every cookie banner reserves space at page load rather than appearing after a 200 millisecond delay.

The twenty-fourth item is interaction to next paint under sustained interaction. We test by recording an interaction trace in DevTools while clicking, scrolling, and typing into any form on the page, and we expect every interaction to complete within 200 milliseconds. The fix path is the same Delay JS toggle from earlier combined with named removals of the WooCommerce cart fragments script on non-cart pages, scoped jQuery handlers in place of document-level delegation, and a switch to Fluent Forms in place of Gravity Forms or WPForms Pro on heavy form pages. We covered the full INP fix tree in our INP article and reference it here for completeness.

The twenty-fifth and final item is cache hit ratio at the edge, which determines whether all the previous twenty-four fixes actually reach the user or whether they get rebuilt at the origin on every visit. We test by reading Cloudflare or Bunny analytics for the cf-cache-status header distribution and expect HIT to dominate at 90 percent or higher on static and HTML alike. The fix path is to enable Cloudflare APO for full-page HTML caching, configure stale-while-revalidate behavior so the edge serves a slightly stale copy while it refreshes, and set Cache Rules that cache logged-out HTML aggressively while bypassing cache for cart, checkout, and account pages.

What this audit looks like as an engagement

A site that passes all twenty-five items lands in the green on Core Web Vitals at the 75th percentile, holds Lighthouse Performance scores above 90 on mobile, and serves repeat visitors in under one second from any geography. A site that passes none of them is the average WordPress build in 2026. The framework above is the entire methodology our team uses on a paid speed audit engagement. If you would rather hand the trace, the fixes, and the post-audit retest to the team that wrote it, the WitsCode 25-Point WordPress Speed Audit ships as a fixed-scope engagement with a written report, a prioritized fix list, the implementation work, and a 30-day re-audit. The framework is free. The 200 audits of pattern recognition that tell us which three items to fix first on your specific build are what you are paying for.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

WordPress Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss wp speed & core web vitals for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.