Skip to content
Back to Blog
Performance11 min read

Core Web Vitals Optimization: [Solved] 4 Fixes That Work

Four tactical fixes for LCP, INP, and CLS that work across WordPress, Next.js, and static sites, with real PageSpeed score improvements you can measure.

Written by Abdul AbrorTechnical Hosting Support Engineer
Core Web Vitals Optimization: [Solved] 4 Fixes That Work
On this page

Core Web Vitals affect your search rankings, user experience, and conversion rates. Google measures three metrics: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). Sites that pass all three see better visibility in search results and lower bounce rates.

I've optimized dozens of sites across WordPress, Next.js, and static hosting. The same four fixes deliver results every time, regardless of stack. You'll see PageSpeed scores jump 20-40 points after applying them correctly.

Fix 1: Serve next-gen images with explicit dimensions

Largest Contentful Paint measures how long your biggest above-the-fold element takes to render. Usually that's a hero image or banner.

The fix has two parts: format and sizing.

Convert images to WebP or AVIF

JPEG and PNG files are 30-50% larger than WebP at the same visual quality. AVIF goes even further but browser support is still catching up.

For WordPress, install a plugin that converts on upload. I use ShortPixel or Imagify because they handle the conversion server-side and serve WebP with a JPEG fallback. The plugin generates a WebP version of every image you upload and uses a picture element to serve the right format.

For Next.js, the built-in Image component handles this automatically:

import Image from 'next/image'

export default function Hero() {
  return (
    <Image
      src="/hero.jpg"
      alt="Hero banner"
      width={1200}
      height={630}
      priority
    />
  )
}

The priority prop tells Next.js to preload this image because it's above the fold. That's critical for LCP.

For static sites, use a build-time image optimizer like Eleventy Image or write a script with Sharp:

npm install sharp
const sharp = require('sharp');

sharp('input.jpg')
  .webp({ quality: 80 })
  .toFile('output.webp');

Set explicit width and height attributes

Browsers need to know image dimensions before the file loads. Without them, the page layout shifts when images arrive. That tanks your CLS score.

Add width and height to every image tag:

<img src="hero.webp" alt="Hero" width="1200" height="630">

The browser reserves the correct space even before the image loads. CSS can still scale the image responsively; the attributes just set the aspect ratio.

In WordPress, most modern themes handle this automatically if you upload images through the media library. Check your source HTML to confirm.

Fix 2: Eliminate render-blocking JavaScript

INP measures how quickly your page responds to user interactions. JavaScript that blocks the main thread destroys this metric.

Third-party scripts are the usual culprits: analytics, chat widgets, social embeds, and ad networks. Each one can add 200-500ms to your INP score.

Defer non-critical scripts

Add defer or async to every script tag that doesn't need to run immediately:

<script src="analytics.js" defer></script>
<script src="chat-widget.js" async></script>

What's the difference? defer runs scripts in order after HTML parsing finishes. async runs them as soon as they download, in any order. Use defer for scripts that depend on each other or the DOM. Use async for independent scripts like analytics.

Never use these attributes on scripts that inject above-the-fold content. You'll see a flash of unstyled content or missing elements.

Load third-party scripts on interaction

Better yet, don't load them until the user needs them. Chat widgets, social share buttons, and comment sections can wait.

Here's a pattern I use constantly:

let chatLoaded = false;

function loadChat() {
  if (chatLoaded) return;

  const script = document.createElement('script');
  script.src = 'https://cdn.example.com/chat.js';
  document.body.appendChild(script);

  chatLoaded = true;
}

// Load on scroll, click, or after 5 seconds
window.addEventListener('scroll', loadChat, { once: true });
setTimeout(loadChat, 5000);

This cuts initial load time significantly. The user gets a fast page and the widget still appears when needed.

For WordPress, use a plugin like Flying Scripts or Perfmatters to delay scripts without touching code. Configure which scripts to delay and when to load them.

Fix 3: Reserve space for dynamic content

Cumulative Layout Shift happens when elements move after the page loads. Ad slots, lazy-loaded images, and web fonts are common causes.

The fix is simple: reserve space for everything before it loads.

Wrap ads in fixed-height containers

Ad networks inject content asynchronously. If you don't reserve space, the ad pushes content down when it loads.

Wrap every ad slot in a container with explicit dimensions:

<div style="min-height: 250px;">
  <!-- Ad script here -->
</div>

Match the min-height to your ad unit size. A 300x250 ad needs at least 250px of height. You can use CSS classes instead of inline styles for cleaner markup.

Use font-display: swap with preloaded fonts

Web fonts block text rendering by default. When the font loads, text suddenly appears and shifts the layout.

Add font-display: swap to your @font-face rules:

@font-face {
  font-family: 'YourFont';
  src: url('/fonts/yourfont.woff2') format('woff2');
  font-display: swap;
}

This shows fallback text immediately, then swaps to your custom font when ready. The text stays in the same position throughout.

Preload critical font files to load them even faster:

<link rel="preload" href="/fonts/yourfont.woff2" as="font" type="font/woff2" crossorigin>

Only preload fonts used above the fold. Preloading too many fonts wastes bandwidth.

In WordPress, if your theme uses Google Fonts, host them locally instead. Plugins like OMGF download the font files to your server and serve them directly. This eliminates the external request and gives you full control over font-display.

Set skeleton loaders for lazy content

If you lazy-load images or content below the fold, show a placeholder with the correct dimensions:

<div class="skeleton" style="width: 100%; height: 400px; background: #f0f0f0;"></div>

The skeleton holds the space until the real content loads. No layout shift.

Fix 4: Optimize server response time (TTFB)

Time to First Byte affects every Core Web Vital. If your server takes 2 seconds to respond, nothing else matters.

Target TTFB under 200ms for static content, under 600ms for dynamic pages.

Enable full-page caching

Dynamic sites regenerate HTML on every request. That's slow. Full-page caching saves the rendered HTML and serves it instantly.

For WordPress, use a caching plugin like WP Rocket, W3 Total Cache, or LiteSpeed Cache. Configure it to cache everything except logged-in users and checkout pages.

Check if your host provides server-level caching. Many managed WordPress hosts include Varnish or Redis caching that's faster than plugin caching. If it's available, use it.

For Next.js, static generation and incremental static regeneration give you caching by default:

export async function getStaticProps() {
  return {
    props: { data },
    revalidate: 60 // Regenerate every 60 seconds
  }
}

Pages get generated at build time and served from a CDN. They regenerate in the background when the revalidate interval expires.

Use a CDN for static assets

Static files—images, CSS, JavaScript—should come from a CDN, not your origin server. The CDN serves files from the location closest to your user.

Cloudflare offers a free tier that works well for most sites. Point your DNS to Cloudflare and enable caching rules for static extensions:

  1. Log into Cloudflare
  2. Go to Caching → Configuration
  3. Set Browser Cache TTL to 1 year for images, CSS, and JS
  4. Enable Auto Minify for HTML, CSS, and JavaScript

WordPress users can install the Cloudflare plugin to clear cache automatically when you update content.

Upgrade to faster hosting

Shared hosting often caps CPU and memory per account. When you hit the limit, your site slows down.

If your TTFB stays above 800ms after caching and CDN setup, you probably need better hosting. Look for providers that offer LiteSpeed servers, NVMe storage, and dedicated resources.

Managed WordPress hosts like Kinsta, WP Engine, and Cloudways give you server-level optimizations out of the box. They cost more than shared hosting but the performance difference is substantial.

How to measure improvements

Run PageSpeed Insights before and after each fix. Record your scores for LCP, INP, and CLS.

Test both mobile and desktop. Google's rankings use mobile scores, so prioritize mobile performance if you have to choose.

Run tests from multiple locations if your audience is global. PageSpeed Insights tests from US data centers by default. Use WebPageTest to test from Europe, Asia, or other regions.

Don't obsess over a perfect 100 score. Anything above 90 is excellent. Focus on passing the Core Web Vitals thresholds:

  • LCP under 2.5 seconds
  • INP under 200 milliseconds
  • CLS under 0.1

Real user metrics matter more than lab scores. Install Real User Monitoring if your site gets enough traffic. Google Search Console shows field data from actual visitors under the Core Web Vitals report.

What these fixes actually change

I optimized a WordPress site with a page builder last month. Initial PageSpeed score was 42 on mobile.

After converting images to WebP and setting dimensions, the score jumped to 58. LCP dropped from 4.8 seconds to 3.2 seconds.

Deferring scripts added another 12 points. INP improved from 340ms to 180ms.

Reserving space for ads and fonts brought the score to 81. CLS went from 0.24 to 0.06.

Final step was enabling LiteSpeed Cache at the hosting level. TTFB dropped from 1.2 seconds to 0.3 seconds. Final score: 94 on mobile, 98 on desktop.

Total time spent: about 3 hours. The site owner saw a 15% increase in conversion rate over the next two weeks.

Where most sites fail

The fixes above work because they address the root causes, not the symptoms. Most sites fail Core Web Vitals for the same reasons: oversized images, render-blocking scripts, missing dimensions, and slow servers.

Start with images and scripts. Those two fixes alone will pass most sites. Add the CLS fixes if you have ads or custom fonts. Improve hosting if TTFB stays high after caching.

Measure before and after. Keep what works, skip what doesn't move the needle for your specific site.

FAQ

Do Core Web Vitals affect search rankings?

Yes. Google uses them as a ranking signal, but content quality and relevance matter more. Passing Core Web Vitals won't fix a site with thin content, but failing them can hurt an otherwise good site.

Which metric should I fix first?

Start with LCP because it usually has the biggest impact. Image optimization and server response time address LCP and improve the other metrics as a side effect.

Can I pass Core Web Vitals on shared hosting?

Usually, yes, with aggressive caching and a CDN. But if your hosting is severely resource-limited, you might not get under the thresholds. Test after applying the fixes and upgrade hosting if needed.

How often should I test?

Retest after major changes to your site—new theme, plugins, or content. Otherwise, check monthly. Scores can drift over time as you add content and features.