LCPCLSINP

Troubleshooting Guide

How to Fix Core Web Vitals Issues in 2026

Technical·24 min read

How to Fix Core Web Vitals Issues: Complete Troubleshooting Guide 2026

Core Web Vitals are confirmed Google ranking signals, and failing them hurts both your search visibility and user experience. This guide covers every common issue for LCP, CLS, and INP with specific code fixes you can implement today. We include before-and-after examples and show you how to use our diagnostic tools to identify exactly what is wrong.

You have checked your Core Web Vitals in Google Search Console and the results are not great. Some of your pages are flagged as "Poor" or "Needs Improvement." Your PageSpeed Insights scores are lower than you would like. You know this matters for SEO, but you are not sure exactly what to fix or where to start.

This guide is the fix-it manual. We are going to walk through each Core Web Vital metric, explain the most common causes of failure, and give you specific code changes and configuration fixes that resolve them. These are not theoretical recommendations. They are the exact fixes we implement during technical SEO engagements that consistently move pages from "Poor" to "Good" in Google's assessment.

Before you start fixing things, run your pages through our Core Web Vitals Calculator to get a baseline measurement and identify which specific metrics are failing. Also check the Page Speed Analyzer for a broader performance picture. Having clear before numbers makes it much easier to verify that your fixes actually worked.

Understanding the Three Core Web Vitals

Core Web Vitals are three specific metrics that Google uses to measure real user experience on your website. Each one captures a different aspect of the page experience.

The Three Metrics and Their Thresholds

MetricMeasuresGoodPoor
LCP (Largest Contentful Paint)Loading speed of main contentUnder 2.5sOver 4.0s
CLS (Cumulative Layout Shift)Visual stabilityUnder 0.1Over 0.25
INP (Interaction to Next Paint)Responsiveness to interactionUnder 200msOver 500ms

Google uses field data (real user measurements) collected over a 28-day rolling window to assess your Core Web Vitals. This means that lab tests in tools like Lighthouse give you an approximation, but the actual scores Google uses for ranking come from real users visiting your site via Chrome. Both data sources are useful: lab data helps you diagnose and debug, while field data tells you how real users experience your pages.

Diagnosing Core Web Vitals Issues

Before you can fix a problem, you need to know exactly what is causing it. Here is a systematic approach to diagnosing Core Web Vitals issues on your site.

Step 1: Check Google Search Console

Navigate to Experience > Core Web Vitals in Google Search Console. This shows you which URLs are classified as Good, Needs Improvement, or Poor for both mobile and desktop. Click into any issue group to see which specific URLs are affected and what type of issue they have.

Step 2: Run Individual Page Analysis

For each affected URL, run it through our Core Web Vitals Calculator and Google PageSpeed Insights. These tools will show you the specific metric values and provide diagnostic information about what is causing each issue. Pay attention to the "Opportunities" and "Diagnostics" sections in PageSpeed Insights, as they point directly to fixable problems.

Step 3: Use Chrome DevTools for Deep Debugging

Open Chrome DevTools (F12), go to the Performance panel, and record a page load. This gives you a detailed timeline of everything that happens during page load, including which resources block rendering, which scripts take the longest, and exactly when layout shifts occur. For LCP issues, look at the "Timings" row to see when LCP fires and what element it corresponds to.

Fixing Largest Contentful Paint (LCP)

LCP measures how long it takes for the largest visible content element to render. This is usually a hero image, a large text block, or a video poster. A slow LCP means users stare at a partially loaded page for too long, and Google considers the page slow.

Fix 1: Optimize Your Hero Image

The most common LCP element is a hero image. If your hero image is a large PNG or JPEG, converting it to WebP or AVIF can reduce file size by 25 to 50 percent without visible quality loss. Also add explicit width and height attributes and use the fetchpriority attribute to tell the browser this image is important.

Before (slow LCP):

<img src="hero-banner.png" alt="Hero banner">

After (optimized LCP):

<img
  src="hero-banner.webp"
  alt="Hero banner"
  width="1200"
  height="600"
  fetchpriority="high"
  decoding="async"
>

Fix 2: Preload the LCP Resource

By default, the browser discovers images only when the HTML parser reaches the img tag. If your LCP image is far down in the HTML or loaded via CSS, the browser discovers it late. Adding a preload hint in the head section tells the browser to start downloading the image immediately.

<head>
  <link
    rel="preload"
    as="image"
    href="hero-banner.webp"
    type="image/webp"
    fetchpriority="high"
  >
</head>

Fix 3: Reduce Server Response Time (TTFB)

Time to First Byte directly affects LCP because the browser cannot start rendering until it receives the HTML. If your TTFB is over 600ms, your LCP is fighting an uphill battle from the start. Common fixes include enabling server-side caching, using a CDN, upgrading your hosting plan, and optimizing database queries.

Server-Side Caching Headers

Add these headers to your server configuration to enable browser and CDN caching:

# Apache (.htaccess)
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/avif "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
</IfModule>

# Nginx
location ~* \.(webp|avif|jpg|png|gif|ico)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

Fix 4: Eliminate Render-Blocking Resources

CSS and JavaScript files loaded in the head section block rendering until they finish downloading and parsing. Move non-critical CSS to the end of the body or load it asynchronously. Defer JavaScript that is not needed for initial render.

Before (render-blocking):

<head>
  <link rel="stylesheet" href="all-styles.css">
  <script src="analytics.js"></script>
  <script src="chat-widget.js"></script>
</head>

After (non-blocking):

<head>
  <!-- Only critical above-the-fold CSS -->
  <link rel="stylesheet" href="critical.css">
  <!-- Defer non-critical CSS -->
  <link rel="preload" href="non-critical.css"
    as="style" onload="this.rel='stylesheet'">
</head>
<body>
  <!-- Content here -->
  <script src="analytics.js" defer></script>
  <script src="chat-widget.js" defer></script>
</body>

Fix 5: Use Responsive Images with srcset

Serving a 2000px-wide image to a mobile user on a 400px screen wastes bandwidth and slows loading. Use the srcset attribute to serve appropriately sized images based on the device.

<img
  src="hero-800.webp"
  srcset="
    hero-400.webp 400w,
    hero-800.webp 800w,
    hero-1200.webp 1200w,
    hero-1600.webp 1600w
  "
  sizes="(max-width: 768px) 100vw, 1200px"
  alt="Hero image"
  width="1200"
  height="600"
  fetchpriority="high"
>

Fixing Cumulative Layout Shift (CLS)

CLS measures how much the visible content shifts around while the page loads. You have experienced this: you start reading a paragraph, and then an ad loads above it and pushes the text down. Or you are about to click a button, and it jumps to a different position. That frustrating experience is exactly what CLS captures.

Fix 1: Set Explicit Dimensions on Images and Videos

The most common cause of layout shift is images and videos without explicit width and height attributes. When the browser first renders the page, it does not know how much space to reserve for an image. Once the image loads, the content below it shifts down. Setting dimensions lets the browser reserve the correct space before the image loads.

Before (causes CLS):

<img src="product-photo.webp" alt="Product photo">
<iframe src="https://youtube.com/embed/video-id"></iframe>

After (no CLS):

<img src="product-photo.webp" alt="Product photo"
  width="800" height="600">

<!-- For responsive videos, use aspect-ratio -->
<div style="aspect-ratio: 16/9; width: 100%;">
  <iframe
    src="https://youtube.com/embed/video-id"
    width="100%" height="100%"
    style="border: 0;"
    loading="lazy"
  ></iframe>
</div>

Fix 2: Reserve Space for Ads and Embeds

Ads are a major source of CLS because they load asynchronously and inject content into the page. The fix is to reserve space for ad slots using CSS min-height, even before the ad loads.

/* Reserve space for ad containers */
.ad-slot-leaderboard {
  min-height: 90px;  /* Standard leaderboard height */
  width: 100%;
  background: #f5f5f5; /* Optional placeholder color */
}

.ad-slot-sidebar {
  min-height: 250px; /* Standard medium rectangle */
  width: 300px;
}

Fix 3: Handle Web Fonts Without Layout Shift

When a custom web font loads, it can cause a Flash of Unstyled Text (FOUT) or a Flash of Invisible Text (FOIT), both of which contribute to CLS. The CSS font-display property controls this behavior. Using font-display: swap is the most common recommendation, but it can cause layout shift if the fallback and web font have very different metrics. The font-display: optional approach eliminates CLS entirely by only using the web font if it loads quickly enough.

/* Option 1: swap with size-adjust (minimal CLS) */
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: swap;
  size-adjust: 105%; /* Adjust to match fallback metrics */
}

/* Option 2: optional (zero CLS) */
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: optional;
}

/* Preload the font for faster loading */
/* Add to <head>:
<link rel="preload" href="custom-font.woff2"
  as="font" type="font/woff2" crossorigin>
*/

Fix 4: Avoid Dynamically Injected Content

Cookie consent banners, notification bars, and promotional popups that inject themselves at the top of the page push all content down and cause large layout shifts. Instead, use overlay-style banners that do not displace existing content, or reserve space for them in your layout from the start.

Before (pushes content down, causes CLS):

<body>
  <!-- This banner injects itself and pushes content down -->
  <div id="cookie-banner" style="display:none;">
    We use cookies...
  </div>
  <header>...</header>
</body>

After (overlay, no CLS):

<body>
  <header>...</header>
  <main>...</main>

  <!-- Fixed position overlay, does not shift content -->
  <div id="cookie-banner" style="
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    background: white;
    box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
  ">
    We use cookies...
  </div>
</body>

Fixing Interaction to Next Paint (INP)

INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. While FID only measured the delay of the first interaction, INP measures the responsiveness of all interactions throughout the page lifecycle. A poor INP means your page feels sluggish when users click buttons, type in forms, or interact with any element.

Fix 1: Break Up Long Tasks

The main thread in the browser handles both JavaScript execution and visual updates. When a long JavaScript task (over 50ms) runs on the main thread, the browser cannot respond to user input until the task completes. Breaking long tasks into smaller chunks using yield points lets the browser process user interactions between chunks.

Before (blocks main thread):

function processLargeDataset(items) {
  // Blocks the main thread for the entire loop
  for (const item of items) {
    processItem(item);
    updateDOM(item);
  }
}

After (yields to main thread):

function yieldToMain() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

async function processLargeDataset(items) {
  const CHUNK_SIZE = 10;
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    for (const item of chunk) {
      processItem(item);
      updateDOM(item);
    }
    // Yield to let browser handle user input
    await yieldToMain();
  }
}

Fix 2: Debounce Event Handlers

Scroll, resize, and input event handlers can fire dozens or hundreds of times per second. If each invocation does expensive work, the page becomes unresponsive. Debouncing limits how often the handler actually executes.

function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// Apply to scroll handler
window.addEventListener('scroll',
  debounce(handleScroll, 100),
  { passive: true }
);

// Apply to search input
searchInput.addEventListener('input',
  debounce(handleSearch, 300)
);

Fix 3: Use requestAnimationFrame for Visual Updates

When an interaction triggers a visual update (such as animating an element or updating a chart), use requestAnimationFrame to schedule the update at the optimal time. This prevents the update from competing with other main thread work and ensures smooth visual feedback.

button.addEventListener('click', () => {
  // Provide immediate visual feedback
  button.classList.add('active');

  // Schedule expensive work for next frame
  requestAnimationFrame(() => {
    updateChart(newData);
    renderResults(filteredItems);
  });
});

Fix 4: Minimize DOM Size

Pages with very large DOM trees (over 1,500 elements) are inherently slower to update because every style recalculation and layout operation has to process more nodes. If your pages have bloated DOM structures from complex templates, deeply nested components, or CMS output, simplifying the DOM can significantly improve INP.

Use the Chrome DevTools Performance panel to look for "Recalculate Style" and "Layout" entries that take more than 10ms. These long entries often correlate with DOM complexity. Consider virtualizing long lists, lazy-loading below-the-fold sections, and reducing unnecessary wrapper elements.

Managing Third-Party Scripts

Third-party scripts are often the biggest hidden cause of Core Web Vitals problems. Analytics trackers, advertising scripts, chat widgets, social media embeds, and A/B testing platforms all compete for main thread time and can degrade all three metrics simultaneously.

Audit Your Third-Party Scripts

Open Chrome DevTools, go to the Network panel, and filter by "Third-party." Count how many third-party scripts load and how much data they transfer. It is not uncommon to find 10 to 20 third-party scripts adding 500KB or more to a page. Each script also requires DNS lookups, TCP connections, and main thread execution time.

Third-Party Script Loading Strategy

  • Critical scripts (analytics, consent): Load with defer, not async. Defer preserves execution order and runs after HTML parsing completes.
  • Non-critical scripts (chat, social): Lazy-load on user interaction. Do not load a chat widget until the user clicks the chat button.
  • Marketing scripts (A/B testing, heatmaps): Load after the page becomes interactive using requestIdleCallback or a scroll-triggered loader.
  • Ad scripts: Use asynchronous ad loading and reserve container space with CSS to prevent CLS.

Lazy-load chat widget on interaction:

// Only load chat widget when user clicks the button
document.getElementById('chat-trigger')
  .addEventListener('click', () => {
    const script = document.createElement('script');
    script.src = 'https://chat-provider.com/widget.js';
    script.defer = true;
    document.body.appendChild(script);
  }, { once: true });

Load non-critical scripts when idle:

// Wait until the browser is idle to load
// marketing and analytics scripts
if ('requestIdleCallback' in window) {
  requestIdleCallback(() => {
    loadHeatmapScript();
    loadSocialWidgets();
  });
} else {
  // Fallback: load after 3 seconds
  setTimeout(() => {
    loadHeatmapScript();
    loadSocialWidgets();
  }, 3000);
}

Testing and Verifying Your Fixes

After implementing fixes, you need to verify they actually worked. Here is the testing workflow we recommend.

Immediate Testing (Lab Data)

Run the fixed pages through our Core Web Vitals Calculator and Google PageSpeed Insights immediately after deploying your changes. Compare the new scores to your baseline measurements. Lab data updates instantly, so you can verify improvements right away.

Also run the Page Speed Analyzer to check for any unintended side effects. Sometimes fixing one metric introduces issues in another. For example, aggressively lazy-loading images can improve LCP but might cause CLS if not implemented with proper width/height attributes.

Wait for Field Data (28 Days)

Google Search Console uses a 28-day rolling average of real user data. This means your field data will not fully reflect your improvements for about a month. Check back after 28 days and compare the Core Web Vitals report to your pre-fix baseline.

Monitor for Regressions

Core Web Vitals can regress due to new content (large unoptimized images), plugin updates (new JavaScript), or third-party script changes (an ad provider updating their code). Build regular checks into your workflow. We recommend monthly audits at minimum, or weekly if you update your site frequently. For a comprehensive approach to ongoing monitoring, see our guide on checking your website SEO score which includes a monthly monitoring routine.

Quick Reference: Fix Priority by Impact

  1. Optimize hero image (usually the single biggest LCP improvement)
  2. Add dimensions to all images and videos (usually the single biggest CLS improvement)
  3. Defer non-critical JavaScript (improves both LCP and INP)
  4. Lazy-load third-party scripts (improves all three metrics)
  5. Enable server-side caching (reduces TTFB, improves LCP)
  6. Preload LCP resource (targeted LCP improvement)
  7. Optimize web fonts (improves CLS and LCP)
  8. Break up long JavaScript tasks (targeted INP improvement)

If you want a professional team to handle your Core Web Vitals optimization, our technical SEO service includes a complete performance audit and implementation of all necessary fixes. We also offer a standalone SEO audit that covers Core Web Vitals as part of a broader site health assessment.

Frequently Asked Questions

What are good Core Web Vitals scores?

Good Core Web Vitals scores are: LCP under 2.5 seconds, CLS under 0.1, and INP under 200 milliseconds. Scores in these ranges are classified as "Good" by Google. Scores between the good and poor thresholds are classified as "Needs Improvement," and scores beyond the poor thresholds indicate significant performance problems that are likely hurting your rankings.

How long does it take for Core Web Vitals fixes to affect rankings?

Core Web Vitals data in Google Search Console is based on a 28-day rolling average of real user data. After implementing fixes, you need to wait approximately 28 days for the field data to reflect the improvements. Ranking changes may take an additional 2 to 4 weeks after the data updates. In total, expect 6 to 8 weeks from fix to ranking impact.

What is the most common cause of poor LCP?

The most common cause of poor LCP is large, unoptimized hero images. Images that are served in older formats like PNG or JPEG instead of WebP or AVIF, that lack proper sizing attributes, or that are not preloaded account for the majority of LCP failures. Server response time (TTFB) is the second most common cause. Use our Core Web Vitals Calculator to diagnose which element is your LCP and what is slowing it down.

Does CLS affect SEO rankings?

Yes. CLS is one of the three Core Web Vitals metrics that Google uses as ranking signals. A CLS score above 0.25 is classified as poor and can negatively impact your search rankings. Beyond the direct ranking impact, high CLS creates a frustrating user experience that increases bounce rates and reduces conversions.

What replaced FID as a Core Web Vital?

Interaction to Next Paint (INP) replaced First Input Delay (FID) as a Core Web Vital in March 2024. INP is a more comprehensive metric because it measures the responsiveness of all user interactions throughout the page lifecycle, not just the first interaction. The good threshold for INP is under 200 milliseconds.

Can third-party scripts cause Core Web Vitals issues?

Yes. Third-party scripts are one of the most common causes of Core Web Vitals problems. Analytics trackers, ad scripts, chat widgets, and social media embeds can all impact LCP by blocking the main thread, cause CLS by injecting content after page load, and degrade INP by making the page unresponsive during script execution. Audit your third-party scripts regularly and defer or lazy-load non-critical ones.

How do I check my Core Web Vitals scores?

You can check Core Web Vitals using Google Search Console for field data across your entire site, Google PageSpeed Insights for both field and lab data on individual URLs, Chrome DevTools Performance panel for detailed lab debugging, or our Core Web Vitals Calculator for a quick analysis with specific recommendations. Use field data from Search Console for the most accurate picture of real user experience.