How to Fix a Slow WordPress Site for Core Web Vitals
To speed up a slow WordPress site and pass Google's Core Web Vitals, you must target the three specific metrics: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). Generic optimization advice will not cut it. Here is the exact technical workflow to optimize each metric.1. Optimizing LCP (Largest Contentful Paint)
LCP measures perceived loading speed. It marks the point in the page loading timeline when the main content has likely loaded. To lower LCP, you must optimize your Time to First Byte (TTFB) and critical path rendering.
- Implement Server-Level Caching: Avoid relying solely on PHP-based WordPress plugins. Configure Nginx FastCGI caching, LiteSpeed Cache, or Redis Object Cache directly on your server to serve static HTML.
- Exclude LCP Images from Lazy Loading: Lazy loading your hero image delays LCP. Ensure your first 1-2 images (usually the featured image or logo) load immediately.
- Preload the LCP Image: Add a preload tag in your document header for the featured image asset.
<link rel="preload" fetchpriority="high" as="image" href="https://yourdomain.com/wp-content/uploads/hero.webp" type="image/webp">
- Optimize PHP OPcache: Ensure OPcache is enabled in your server's
php.inito store precompiled script bytecode in memory.
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
2. Improving INP (Interaction to Next Paint)
INP measures page responsiveness to user inputs (clicks, taps, keypresses). High INP is caused by JavaScript blocking the main thread.
- Defer Non-Essential JavaScript: Move non-critical scripts (analytics, ads, tag managers) out of the critical rendering path. Add the
deferorasyncattribute to script tags.
Add this filter to your theme's functions.php to automatically defer non-essential scripts:
add_filter('script_loader_tag', function($tag, $handle, $src) {
if (is_admin()) return $tag;
if (strpos($handle, 'jquery.min.js') !== false) return $tag; // Keep jQuery synchronous if required by plugins
return str_replace(' src', ' defer="defer" src', $tag);
}, 10, 3);
- Reduce DOM Size: Page builders like Elementor or Divi generate deeply nested HTML tags. Keep your total DOM nodes under 1,000. Avoid nested columns and sections where possible.
3. Eliminating CLS (Cumulative Layout Shift)
CLS measures visual stability. It tracks unexpected layout shifts that occur while a page is downloading and rendering.
- Set Explicit Dimensions on Media: Always declare
widthandheightattributes on images, videos, and iframe embeds. This allows the browser to reserve the correct aspect ratio box before the asset downloads.
<img src="image.webp" width="800" height="450" alt="Optimized Image">
- Reserve Space for Dynamic Elements: If you load late-rendering elements like Google Adsense blocks or AJAX-loaded content, wrap them in a container div with a min-height specified in CSS.
.ad-container {
min-height: 250px;
}
- Optimize Font Loading: Prevent Flash of Invisible Text (FOIT) and Flash of Unstyled Text (FOUT) by using the CSS
font-display: swap;property and preloading local web fonts.
4. Clean Up Autoloaded Database Queries
A bloated database increases TTFB, dragging down your LCP score. WordPress queries the wp_options table on every page load. If your autoloaded data exceeds 1MB, your site will slow down.
Run this SQL query in phpMyAdmin to identify the size of your autoloaded options:
SELECT SUM(LENGTH(option_value)) as autoload_size_bytes FROM wp_options WHERE autoload = 'yes';
If this number is high, find the largest autoloaded options using the following query, and delete or disable autoloading for plugins you no longer use:
SELECT option_name, length(option_value) AS option_len
FROM wp_options
WHERE autoload = 'yes'
ORDER BY option_len DESC
LIMIT 20;
Need this done fast? order a speed audit on Kwork.
Need help with this?
I take on freelance fixes and builds in this area.