Core Web Vitals Fix Checklist for WordPress Sites (2026)
Complete 30-point Core Web Vitals fix checklist for WordPress. Plugin audit, image optimization, cache configuration, font loading, and hosting requirements.
A WordPress site running WooCommerce loads 47 JavaScript files, 12 CSS stylesheets, and 6 render-blocking Google Fonts requests on every page — before a single product image appears. The homepage LCP clocks in at 5.2 seconds on mobile, INP registers at 420ms from the bloated jQuery event queue, and CLS hits 0.28 from an ad plugin injecting a banner after page paint. All three Core Web Vitals are deep red, yet the site owner has no idea where to start because the WordPress ecosystem distributes performance problems across themes, plugins, hosting, and configuration layers.
WordPress powers 43% of the web, and its plugin architecture makes it uniquely vulnerable to CWV failures — each plugin can inject scripts, styles, and DOM elements without coordination. This checklist provides the exact 30-point audit sequence for fixing CWV on WordPress, organized from highest-impact (P0) to maintenance-level (P2) items, with specific plugin names, functions.php snippets, and .htaccess configurations.
Pre-Flight: Measure Your Baseline
Before changing anything, capture your current CWV scores for comparison:
# Measure baseline CWV for your key pages
npx lighthouse https://your-wordpress-site.com \
--output=json \
--output-path=./baseline-home.json \
--throttling-method=provided \
--screenEmulation.mobile
# Extract key metrics
cat baseline-home.json | jq '{
LCP: .audits["largest-contentful-paint"].numericValue,
TBT: .audits["total-blocking-time"].numericValue,
CLS: .audits["cumulative-layout-shift"].numericValue,
Performance: .categories.performance.score
}'Record these values. After each optimization group, re-measure to quantify the improvement.
P0 Critical — Fix These First (Biggest Impact)
☐ 1. Audit and Remove Unnecessary Plugins
Every active plugin adds JavaScript, CSS, or database queries to every page load. The average WordPress site has 20–30 plugins; most sites only need 8–12.
# List all active plugins via WP-CLI
wp plugin list --status=active --fields=name,version,update
# Deactivate and test removal of suspect plugins
wp plugin deactivate plugin-slug
# Test site functionality → if nothing breaks, delete it
wp plugin delete plugin-slugRed flag plugins to audit first:
| Plugin Category | Common Culprits | CWV Impact |
|---|---|---|
| Social sharing buttons | AddThis, ShareThis, Social Warfare | +80–200ms INP (JavaScript listeners on every element) |
| Slider/carousel plugins | Revolution Slider, Slider Hero | +300–800ms LCP (massive JS/CSS bundles) |
| Page builders (heavy) | Elementor (with all widgets active) | +200–400ms TBT (renders entire widget library) |
| Contact form plugins | Contact Form 7 (loaded on every page) | +50–100ms per page (JS loaded site-wide for a single page form) |
| SEO plugins (over-featured) | All-in-one SEO suites with unused modules | +30–80ms (serializing settings on every request) |
💡 Engineering Rule of Thumb: Perform a plugin audit quarterly. For each plugin, ask: "Is this plugin loaded on every page, and does it need to be?" If a contact form plugin loads its JavaScript on your homepage, that's wasted performance. Use conditional loading or asset cleanup plugins to restrict plugin assets to the pages that need them.
☐ 2. Switch to a Performance-Optimized Theme
Your WordPress theme's code quality has more impact on CWV than any plugin optimization. Heavy themes ship 300–500KB of JavaScript and CSS on every page regardless of what the page actually uses.
Recommended lightweight themes:
| Theme | Total JS | Total CSS | CWV Impact |
|---|---|---|---|
| GeneratePress | ~30KB | ~30KB | LCP < 2s baseline |
| Astra (lightweight mode) | ~45KB | ~40KB | LCP < 2.2s baseline |
| Kadence | ~40KB | ~35KB | LCP < 2.1s baseline |
| Blocksy | ~50KB | ~45KB | LCP < 2.3s baseline |
| Typical "multipurpose" theme | 300–500KB | 200–400KB | LCP > 4s baseline |
☐ 3. Install and Configure a Caching Plugin
WordPress generates every page dynamically via PHP + MySQL queries by default. A caching plugin serves pre-built HTML files, eliminating server-side processing time and cutting TTFB from 800ms+ to under 200ms.
// wp-config.php — enable WordPress page caching
define('WP_CACHE', true);
// Recommended caching plugins (choose ONE):
// 1. WP Super Cache (free, simple, maintained by Automattic)
// 2. W3 Total Cache (free, advanced configuration)
// 3. WP Rocket (paid, best out-of-box configuration)
// 4. LiteSpeed Cache (free, if your host runs LiteSpeed server)Minimum caching configuration checklist:
- ☐ Page caching enabled (serves static HTML to visitors)
- ☐ Browser caching enabled (sets
Cache-Controlheaders for static assets) - ☐ GZIP/Brotli compression enabled
- ☐ Database query caching enabled (reduces MySQL query time)
- ☐ Object caching enabled (if Redis/Memcached available on host)
☐ 4. Optimize the LCP Image
Identify your LCP element (usually the hero image or featured image above the fold), then optimize it:
// functions.php — Preload the LCP image and set fetchpriority
function preload_lcp_image() {
if (is_front_page()) {
// Replace with your actual hero image path
echo '<link rel="preload" href="' . get_template_directory_uri() .
'/images/hero-banner.webp" as="image" type="image/webp" fetchpriority="high" />';
}
}
add_action('wp_head', 'preload_lcp_image', 1);
// Disable lazy loading on the first image (likely the LCP element)
function disable_lazy_load_first_image($attr, $attachment, $size) {
// WordPress 5.9+ lazy loads all images by default
// Remove lazy loading from the first image on the page
static $first_image = true;
if ($first_image) {
$attr['loading'] = 'eager';
$attr['fetchpriority'] = 'high';
$first_image = false;
}
return $attr;
}
add_filter('wp_get_attachment_image_attributes', 'disable_lazy_load_first_image', 10, 3);☐ 5. Enable WebP/AVIF Image Conversion
Convert all uploaded images to next-gen formats:
# Option 1: Use ShortPixel plugin (auto-converts on upload)
wp plugin install shortpixel-image-optimiser --activate
# Option 2: Use Imagify plugin
wp plugin install imagify --activate
# Option 3: Server-side with .htaccess (no plugin needed)
# Requires mod_rewrite and WebP versions of all images# .htaccess — Serve WebP images when browser supports them
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png|gif)$
RewriteCond %1.webp -f
RewriteRule (.+)\.(jpe?g|png|gif)$ $1.webp [T=image/webp,L]
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(jpe?g|png|gif)$">
Header append Vary Accept
</FilesMatch>
</IfModule>P1 Major — Significant Impact Fixes
☐ 6. Defer Non-Critical JavaScript
// functions.php — defer all non-critical JS
function defer_non_critical_scripts($tag, $handle, $src) {
// Don't defer jQuery core (many plugins depend on it inline)
$no_defer = ['jquery-core', 'jquery-migrate', 'wp-polyfill'];
if (in_array($handle, $no_defer)) {
return $tag;
}
// Add defer attribute to all other scripts
return str_replace(' src', ' defer src', $tag);
}
add_filter('script_loader_tag', 'defer_non_critical_scripts', 10, 3);☐ 7. Remove Unused CSS (Critical CSS Extraction)
WordPress themes and page builders load their entire CSS library on every page. Extract only the CSS used above the fold:
# Generate critical CSS for your key templates
npx critical https://your-site.com \
--base ./ \
--inline \
--width 1300 \
--height 900 \
--css wp-content/themes/your-theme/style.css \
> critical.cssPlugin approach: WP Rocket and Perfmatters both include automatic Critical CSS generation that extracts above-fold CSS and defers the rest.
☐ 8. Optimize Font Loading
// functions.php — Self-host Google Fonts instead of loading from CDN
function dequeue_google_fonts() {
// Remove Google Fonts loaded by theme/plugins
wp_dequeue_style('google-fonts');
wp_deregister_style('google-fonts');
}
add_action('wp_enqueue_scripts', 'dequeue_google_fonts', 100);
// Enqueue self-hosted fonts with font-display: swap
function enqueue_local_fonts() {
wp_enqueue_style(
'local-fonts',
get_template_directory_uri() . '/fonts/fonts.css',
[],
'1.0'
);
}
add_action('wp_enqueue_scripts', 'enqueue_local_fonts');/* fonts/fonts.css — Self-hosted with swap and size-adjust */
@font-face {
font-family: 'Inter';
src: url('inter-latin-400.woff2') format('woff2');
font-display: swap;
font-weight: 400;
font-style: normal;
unicode-range: U+0000-00FF, U+0131, U+0152-0153;
}
@font-face {
font-family: 'Inter Fallback';
src: local('Arial');
size-adjust: 107.64%;
ascent-override: 96.88%;
descent-override: 24.15%;
line-gap-override: 0%;
}☐ 9. Set Explicit Image Dimensions
WordPress 5.5+ automatically adds width and height to images inserted via the editor. However, images added via custom theme code, page builders, or shortcodes may lack these attributes.
// functions.php — ensure all images have width/height
function add_image_dimensions($content) {
// Find images without width/height attributes
$content = preg_replace_callback(
'/<img([^>]*)>/i',
function ($matches) {
$img = $matches[0];
// Skip if width and height already present
if (preg_match('/\bwidth\s*=/', $img) && preg_match('/\bheight\s*=/', $img)) {
return $img;
}
// Extract src and get dimensions
if (preg_match('/src=["\']([^"\']+)["\']/i', $img, $src_match)) {
$image_path = str_replace(
site_url('/'),
ABSPATH,
$src_match[1]
);
if (file_exists($image_path)) {
$size = getimagesize($image_path);
if ($size) {
$img = str_replace(
'<img',
'<img width="' . $size[0] . '" height="' . $size[1] . '"',
$img
);
}
}
}
return $img;
},
$content
);
return $content;
}
add_filter('the_content', 'add_image_dimensions');☐ 10. Limit Post Revisions and Optimize Database
// wp-config.php — limit revisions to reduce database bloat
define('WP_POST_REVISIONS', 5);
define('AUTOSAVE_INTERVAL', 120); // Autosave every 2 minutes instead of 60s
// Disable unused WordPress features that add JS/CSS
define('DISALLOW_FILE_EDIT', true); // Disable theme/plugin editor# Clean up database (removes revisions, drafts, spam, transients)
wp post delete $(wp post list --post_type='revision' --format=ids) --force
wp transient delete --expired
wp db optimize☐ 11. Remove jQuery Dependency (If Possible)
WordPress still loads jQuery by default for backward compatibility. If your theme and active plugins don't require jQuery:
// functions.php — dequeue jQuery if not needed by active plugins
function remove_jquery_if_unnecessary() {
if (!is_admin()) {
wp_deregister_script('jquery');
wp_deregister_script('jquery-migrate');
}
}
add_action('wp_enqueue_scripts', 'remove_jquery_if_unnecessary');☐ 12. Preconnect to Required Third-Party Origins
// functions.php — add preconnect hints for third-party domains
function add_preconnect_hints() {
// Only add if you actually load resources from these origins
echo '<link rel="preconnect" href="https://fonts.googleapis.com" />';
echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />';
// Add your CDN if applicable
echo '<link rel="preconnect" href="https://cdn.your-domain.com" />';
}
add_action('wp_head', 'add_preconnect_hints', 1);☐ 13. Configure Browser Caching Headers
# .htaccess — aggressive browser caching for static assets
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/avif "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(woff2|avif|webp|jpg|png|svg)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>☐ 14. Enable GZIP/Brotli Compression
# .htaccess — enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE text/plain
</IfModule>☐ 15. Reserve Space for Ad Containers
// functions.php — Add min-height to ad containers
function reserve_ad_space() {
echo '<style>
.ad-slot-leaderboard { min-height: 90px; contain: layout style; }
.ad-slot-sidebar { min-height: 250px; contain: layout style; }
.ad-slot-in-content { min-height: 250px; contain: layout style; }
</style>';
}
add_action('wp_head', 'reserve_ad_space');P2 Moderate — Maintenance & Polish
☐ 16. Use a CDN for Static Assets
Configure your CDN (Cloudflare, BunnyCDN, StackPath) to serve images, CSS, JS, and fonts from edge servers:
// wp-config.php — offload static assets to CDN
define('WP_CONTENT_URL', 'https://cdn.your-domain.com/wp-content');☐ 17. Disable WordPress Emoji Script
// functions.php — remove emoji script (saves ~10KB per page)
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
remove_action('admin_print_scripts', 'print_emoji_detection_script');
remove_action('admin_print_styles', 'print_emoji_styles');☐ 18. Disable WordPress Block Library CSS (If Not Using Gutenberg)
// functions.php — remove block library CSS if using classic editor
function remove_block_css() {
wp_dequeue_style('wp-block-library');
wp_dequeue_style('wp-block-library-theme');
wp_dequeue_style('wc-blocks-style'); // WooCommerce blocks
wp_dequeue_style('global-styles');
}
add_action('wp_enqueue_scripts', 'remove_block_css', 100);☐ 19. Limit Heartbeat API Frequency
// functions.php — reduce Heartbeat API polling frequency
function limit_heartbeat($settings) {
$settings['interval'] = 60; // Default is 15 seconds
return $settings;
}
add_filter('heartbeat_settings', 'limit_heartbeat');
// Disable Heartbeat on frontend entirely
function disable_frontend_heartbeat() {
if (!is_admin()) {
wp_deregister_script('heartbeat');
}
}
add_action('init', 'disable_frontend_heartbeat', 1);☐ 20. Implement DNS Prefetching
// functions.php — DNS prefetch for external resources
function add_dns_prefetch() {
echo '<link rel="dns-prefetch" href="//www.google-analytics.com" />';
echo '<link rel="dns-prefetch" href="//www.googletagmanager.com" />';
}
add_action('wp_head', 'add_dns_prefetch', 0);☐ 21–25. Additional Optimizations
- ☐ 21. Disable pingbacks and trackbacks (
Settings → Discussion) - ☐ 22. Limit RSS feed items to 10 (
Settings → Reading) - ☐ 23. Use PHP 8.2+ (30–50% faster than PHP 7.4)
- ☐ 24. Enable OPcache on your hosting (
opcache.enable=1) - ☐ 25. Use MySQL query caching or Redis object cache
☐ 26–30. Hosting and Infrastructure
- ☐ 26. Use managed WordPress hosting (Cloudways, Kinsta, WP Engine) vs shared hosting
- ☐ 27. Ensure your server is geographically close to your primary audience
- ☐ 28. Enable HTTP/2 or HTTP/3 on your server
- ☐ 29. Monitor uptime and TTFB weekly
- ☐ 30. Set up staging environment for testing changes before production
How BugViso Audits WordPress Sites for CWV Issues
WordPress sites have unique performance challenges from their plugin ecosystem, theme complexity, and hosting configurations. Manual testing across all the checklist items above takes hours per site.
BugViso's audit engine provides automated coverage of the critical CWV dimensions:
- Core Web Vitals capture (LCP, CLS, INP, FCP, TTFB) using the official
web-vitalslibrary on every crawled page, providing the exact per-page scores for prioritizing fixes. - Network throttling simulation under Slow 3G and Fast 3G conditions, exposing how WordPress plugin bloat affects performance on the mobile connections where most users browse.
- Code coverage analysis via CDP JavaScript and CSS coverage tracking, identifying unused plugin assets loaded on pages that don't need them — the data that justifies plugin removal or conditional loading.
- Broken link validation via concurrent HTTPX link probing across all internal and external links, catching the broken links that accumulate from plugin deactivation and content restructuring.
- Security header audit checking for HSTS, CSP, and X-Frame-Options — configurations often misconfigured on WordPress sites running behind shared hosting.
- Accessibility audit via the self-hosted axe-core engine, identifying WCAG 2.1 violations that WordPress themes and plugins commonly introduce.
Frequently Asked Questions
Which WordPress caching plugin is best for CWV?
WP Rocket provides the best out-of-box CWV configuration: page caching, CSS/JS minification, critical CSS generation, lazy loading with native loading attribute, and database optimization. If budget is a concern, W3 Total Cache (free) offers equivalent functionality but requires more manual configuration.
Does my hosting provider affect CWV scores?
Yes, significantly. Shared hosting with 200+ sites on one server produces TTFB of 800ms–2s, which directly inflates LCP. Managed WordPress hosts (Kinsta, Cloudways, WP Engine) deliver TTFB under 300ms with built-in caching, CDN, and PHP optimization — often improving LCP by 500ms–1s without any code changes.
Can I pass CWV with a page builder like Elementor?
Yes, but it requires careful configuration. Elementor's default setup loads its entire widget library (500KB+) on every page. Optimize by: enabling "Improved Asset Loading" in Elementor settings, disabling unused widgets, using Elementor's built-in CSS Print Method "Internal Embedding", and avoiding widget-heavy designs with 20+ Elementor sections per page.
How many plugins is too many?
There is no magic number, but a well-optimized WordPress site rarely needs more than 12–15 active plugins. Each additional plugin adds JavaScript, CSS, and database queries. The issue is not the count but the total asset weight — 15 lightweight plugins can be faster than 5 bloated ones. Audit by performance impact, not count.
How do I measure CWV improvement after making changes?
Wait at least 28 days after deploying changes for CrUX field data to update. In the meantime, use Lighthouse or WebPageTest for lab measurements. Compare pre-optimization and post-optimization Lighthouse reports to quantify improvement. For site-wide verification, a free BugViso audit captures per-page CWV scores across your entire WordPress site in a single automated crawl.
See where your site stands
Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.