Your Lighthouse score is 95. Your Search Console still says "Poor."
You ran Lighthouse, chased the number to 95, shipped it, and a month later Search Console still flags the URL as "Poor." Nothing you did moved it. This is the most common Core Web Vitals misunderstanding in 2026: the score you optimized and the score that ranks you are two different measurements, taken from two different places, on two different timelines.
Core Web Vitals are a confirmed Google ranking signal, though a lightweight one: closer to a tiebreaker between otherwise-comparable results than a heavy factor. They have been part of the page experience system since 2021. But the scores Google actually uses come from real users, collected over a rolling 28-day window. You cannot optimize that number directly. What you can do is fix the page-level mistakes that cause it, and almost all of them are visible in your HTML before a single user ever loads the page.
What's in this post
- Lab data vs. field data, and which one ranks you
- The three metrics, and the thresholds that matter
- The four page-level causes behind a bad LCP and CLS
- Why TTFB caps everything above it
- How to find all of this in one scan
Lab data vs. field data, and which one ranks you
There are two kinds of Core Web Vitals data, and confusing them is why so much optimization work goes nowhere.
Lab data is a single synthetic measurement: one load, in a controlled environment, on a simulated device. Lighthouse, PageSpeed Insights' "Analyze" mode, and web-vitals in dev all produce lab data. It is instant, repeatable, and great for debugging.
Field data is what real Chrome users experienced on your page, aggregated into the Chrome User Experience Report (CrUX). Google scores you at the 75th percentile of real users over a rolling 28-day window, which means a fix you shipped today will not fully show up in Search Console for nearly a month. This field data is what feeds the page experience ranking signal. Lab data does not rank you.
So the loop most developers run (tweak, re-run Lighthouse, celebrate the number) optimizes the measurement nobody ranks on. The number that does rank you only moves when real-user experience improves, and real-user experience only improves when you fix the underlying page mechanics. Fix the causes, and the field score follows.
The three metrics, and the thresholds that matter
Core Web Vitals is three metrics. As of March 2024, INP replaced FID as the responsiveness vital, so any guide still talking about First Input Delay is out of date.
METRIC | MEASURES | GOOD (p75) | NEEDS WORK | POOR
------------------------------------------------------------------------------------
LCP Largest Contentful Paint | loading speed | <= 2.5 s | 2.5-4.0 s | > 4.0 s
CLS Cumulative Layout Shift | visual stability | <= 0.1 | 0.1-0.25 | > 0.25
INP Interaction to Next Paint | responsiveness | <= 200 ms | 200-500 ms | > 500 ms
A page is "good" only when all three clear the bar at the 75th percentile. One poor metric is enough to fail the URL. Thresholds are published by Google on web.dev and have held steady since INP's promotion.
The useful insight: LCP and CLS are overwhelmingly determined by how your HTML is constructed, namely what loads first, what blocks rendering, and whether elements reserve their space. Those are static, inspectable properties of the page. You do not need 28 days of field data to find them.
INP is the exception. Because it measures responsiveness to real interactions, its causes (heavy JavaScript, long main-thread tasks, oversized event handlers) only surface once a user actually clicks. You can't fully diagnose INP from static HTML the way you can LCP and CLS. That's why the page-level causes below focus on the two vitals you can catch before launch; INP still needs real-user data or runtime profiling to pin down.
The four page-level causes behind a bad LCP and CLS
In practice, four mistakes account for the large majority of failing LCP and CLS scores. All four are detectable in the page's HTML.
1. Lazy-loading the hero image
This is the single most common LCP killer. The reasoning sounds right ("lazy-load images for performance"), so a developer adds loading="lazy" to every image, including the big above-the-fold hero that is almost always the LCP element itself. Deferring it directly delays the metric you are trying to improve.
<!-- Wrong: the LCP element is deferred and de-prioritized -->
<img src="/hero.jpg" loading="lazy" alt="..." />
<!-- Right: load it eagerly and tell the browser it's important -->
<img src="/hero.jpg" fetchpriority="high" alt="..." />
Rule of thumb: never lazy-load anything above the fold, and add fetchpriority="high" to the LCP image so the browser fetches it ahead of less important resources.
2. Render-blocking scripts and styles in the <head>
A synchronous <script> or a large stylesheet in the <head> blocks the browser from painting anything until it downloads and runs. Every blocking resource pushes back the moment your largest element can appear, so it inflates LCP for the whole page, not just the script's own feature.
<!-- Blocks rendering until fetched and executed -->
<script src="/analytics.js"></script>
<!-- Non-blocking: defer runs after parse, async runs out-of-band -->
<script src="/analytics.js" defer></script>
Defer or async anything that is not required for first paint, and inline only the critical CSS the above-the-fold view actually needs.
3. Images without width and height
When an image has no dimensions, the browser does not know how much space to reserve, so it paints the page, then reflows everything downward when the image arrives. That jump is Cumulative Layout Shift, and it is the most preventable CLS cause there is.
<!-- Causes layout shift: no reserved space -->
<img src="/chart.png" alt="..." />
<!-- Stable: browser reserves the box before the image loads -->
<img src="/chart.png" width="1200" height="630" alt="..." />
Always set width and height (or a CSS aspect-ratio) on every image, every embed, and every ad slot. The same applies to fonts and injected banners: anything that arrives late and pushes content around.
4. Below-the-fold images that aren't lazy-loaded
The inverse of mistake #1. Images far down the page that load eagerly compete for bandwidth with your hero, slowing the LCP fetch. Below-the-fold images should carry loading="lazy" so the browser spends its early bandwidth on what is visible first.
IMAGE POSITION | loading | fetchpriority
---------------------------------------------------------
LCP / hero (above fold) | omit (eager) | high
Other above-the-fold | omit (eager) | default
Below the fold | lazy | default
Why TTFB caps everything above it
Time to First Byte, how long your server takes to start responding, is not a Core Web Vital itself, but it is the floor under LCP. Your largest element cannot start painting until the first byte arrives, so a slow TTFB sets a hard ceiling on how fast LCP can ever be, no matter how perfectly the front end is tuned. web.dev considers TTFB good at 800 ms or under.
If your TTFB is 1.5 seconds, no amount of fetchpriority will get LCP under 2.5 seconds, because you have spent 60% of the budget before any HTML ships. Common culprits: uncached server-rendered pages, slow database queries on the critical path, no CDN, and redirect chains that add a full round trip before the real response. (A misconfigured redirect can quietly add hundreds of milliseconds, worth checking with a redirect checker if your TTFB is high.)
The order of operations that actually works: get TTFB under control first, then fix the LCP element, then eliminate layout shift. Optimizing front-end paint while the server takes 1.5 seconds to answer is rearranging deck chairs.
How to find all of this in one scan
Every cause above is visible in the page's HTML and response timing, with no 28-day wait required. But checking them by hand means measuring TTFB, hunting for loading="lazy" on the hero, auditing every image for dimensions, and reading the <head> for render-blocking resources, on every page you care about.
The LintPage Core Web Vitals Checker does all of it in one request: it measures your live TTFB, flags an above-the-fold image that's lazy-loaded, lists render-blocking scripts and stylesheets in the <head>, finds images missing width/height, and catches below-the-fold images that forgot loading="lazy". It reports the page-level causes of poor LCP and CLS so you can fix them before they show up as a "Poor" verdict in Search Console a month from now. (INP aside: that one still needs real-user data, as noted above.)
Core Web Vitals issues rarely travel alone: the same pages that lazy-load their hero tend to have other pre-launch problems too. It is worth running the full pre-launch SEO checklist and the complete audit while you're at it.
The 30-second version
The score you chase in Lighthouse (lab data) is not the score that ranks you (field data: real users, 75th percentile, 28-day window). You cannot optimize the field score directly. You fix the page-level causes and wait for it to follow. Four causes explain most failing LCP and CLS: lazy-loaded hero images, render-blocking resources in the <head>, images without dimensions, and below-the-fold images that load eagerly. TTFB sits under all of it, so get it below 800 ms first, because it caps how fast LCP can ever be. INP is the one vital you can't catch from static HTML: it needs real interaction data. Everything else is in your HTML right now, today, with no waiting required. Find them, fix them, and let the 28-day window do the rest.