All articles
PerformanceJune 28, 20268 min read

Core Web Vitals and the Boring Health Checks That Break Sites

LCP, INP and CLS with the real thresholds and their actual causes — plus the unglamorous checks (SSL expiry, broken links, robots.txt) that take a site down while you optimise images.

By Niraj Kumar

Website health is the least interesting subject in growth, and it is the one that takes sites down.

Not slowly, not as a gradual erosion of ranking signals — down. An expired SSL certificate on a Saturday morning shows every visitor a full-page browser warning telling them your site is dangerous. No traffic, no conversions, no notification, until someone happens to check. Meanwhile the industry writes another article about shaving 40ms off a JavaScript bundle.

This post covers both halves: the Core Web Vitals that everyone talks about, with their real thresholds and their actual causes, and the boring checks that nobody talks about and that fail far more expensively.

Part 1: Core Web Vitals, properly

Three metrics. Google uses them as a ranking signal, but that's honestly the less interesting reason to care — they're a decent proxy for whether your site feels broken, and a site that feels broken doesn't convert regardless of where it ranks.

Metric Good Needs work Poor What it measures
LCP ≤ 2.5s 2.5–4.0s > 4.0s Time until the biggest element renders
INP ≤ 200ms 200–500ms > 500ms Time from a tap until the page responds
CLS ≤ 0.1 0.1–0.25 > 0.25 How much the layout jumps around

The threshold is measured at the 75th percentile of real users. That detail matters more than the numbers. It means 25% of your visitors are allowed to have a worse experience than the threshold — and it means your laptop is not the measurement. Your laptop is fast, on good wifi, with a warm cache. Your customer is on a three-year-old Android, on a train, with fifteen tabs open.

LCP: they're waiting for your hero image

Largest Contentful Paint is how long until the biggest thing on screen — nearly always your hero image or headline — actually appears.

In practice, LCP is an image problem. Every time. The order I'd check:

  • The hero image is enormous. A 2.4MB PNG that should be a 120KB WebP. This is the single most common cause of a failing LCP on the entire web, and it's a ten-minute fix.
  • The image isn't preloaded, so the browser only discovers it after parsing CSS, by which point it's lost half a second doing nothing.
  • It's lazy-loaded. Someone added loading="lazy" to every image site-wide. On the hero — the one element that defines LCP — this deliberately delays the one thing you're being measured on. Never lazy-load above the fold.
  • Render-blocking resources. A synchronous third-party script in the <head> stops the page dead until it loads. Chat widgets and tag managers are the usual suspects.
  • Web fonts hiding the text. With the wrong font-display, the browser renders nothing for up to three seconds waiting for a font. Use font-display: swap and accept the flash.

INP: the button that feels dead

Interaction to Next Paint measures the lag between a user tapping something and the page visibly reacting. It replaced First Input Delay because FID only measured the first interaction, which flattered everyone.

This is the metric that makes a site feel cheap. A button that does nothing for 400ms gets pressed again — and now you've got a double form submission, and the user thinks your site is broken, because from where they're sitting it is.

The cause is almost always too much JavaScript running on the main thread. The browser is single-threaded for this: while your analytics script, your A/B testing snippet, and your cookie consent manager are all doing work, the browser physically cannot repaint. Break up long tasks, defer what isn't needed for the first render, and audit your third-party scripts with genuine hostility — each one is a tax paid by every visitor forever.

CLS: the thing that makes people misclick

Cumulative Layout Shift measures how much content jumps around while loading. Its signature experience: you go to tap a link, an ad loads above it, everything slides down, and you tap something else entirely.

The causes are a short and completely fixable list:

  • Images without width and height. The browser doesn't know how much room to reserve, so it reserves none, then shoves everything down when the image arrives. Always set dimensions (or aspect-ratio). This is most of CLS, most of the time.
  • Ads, embeds and iframes with no reserved space. Same problem. Reserve the box.
  • Web fonts causing reflow when the fallback font has different metrics.
  • Content injected at the top — a cookie banner, a promo bar — after the page has already painted.

Part 2: The checks nobody runs

Now the unglamorous half. These fail rarely, and when they fail they cost more than every Core Web Vital combined.

SSL certificate expiry

Certificates expire. Auto-renewal exists and mostly works — until the renewal cron fails silently, or the domain validation breaks, or Let's Encrypt changes something, or the certificate is on a load balancer nobody remembers configuring.

When it expires, every browser shows a full-page interstitial saying your site is not secure. Not a subtle degradation. A wall. Conversion goes to approximately zero, and you find out from a customer, usually on a Sunday.

Check the expiry date, and alert 14 days before it. Not on the day. Fourteen days, because you need a working week to fix a certificate problem you don't understand yet.

echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
  | openssl x509 -noout -dates

Uptime — and what you're actually pinging

Most uptime monitoring checks the homepage and looks for a 200. That misses the two failures that actually cost you money:

  • The homepage is up and the checkout is down. Different service, different database, no alert. Monitor the pages that make money, not the one that's easiest to check.
  • It returns 200 and serves a broken page. A JavaScript error means a blank white screen with a perfectly healthy HTTP status. Your monitor is delighted.

Check for a string on the page — a word that only appears when it rendered properly — not just a status code. And there's a nastier version of this: a deployment behind an SSO or password wall answers 200 to your monitor while the public gets a login screen. Status-code monitoring says everything's fine. Nobody can use the site.

Broken links

Links rot. A page gets renamed, a product is retired, an external site restructures, and now you have 404s scattered across your site.

Internal 404s are the ones to care about: they waste crawl budget, they strand the internal links that pass authority, and they're a visible sign of neglect to a human who clicks one. External broken links are lower stakes but they're the fastest way to look like you stopped maintaining the site in 2023.

robots.txt and sitemap.xml

Two small files that can quietly delete you from search:

  • robots.txt — one over-broad Disallow and you've blocked a section, or the whole site. The staging file that shipped to production is a genuine, common, catastrophic bug. And note this file is now doing double duty: it's also what controls whether AI answer engines can read you.
  • sitemap.xml — generated once at launch, never regenerated, now listing pages that don't exist and omitting every page you've published since.

Neither will ever throw an error. Both will silently cost you traffic for years.

Why this stays broken

None of the above is difficult. It's all a ten-minute fix and a curl command. So why is it broken on so many sites?

Because there's no moment at which anyone notices. A certificate has 90 days left, then 60, then 9, and no single day is the day someone checks. An image is fine at launch and someone swaps in a bigger one eighteen months later. A Disallow line is added for a good reason and outlives the reason.

Every check here is trivially automatable and none of them is memorable. Which is the entire argument for running them on a schedule rather than in a heroic quarterly audit — the value isn't in the sophistication of the check, it's in the fact that something looked, on a Tuesday, when you were busy. That's the same argument as monitoring competitors and for the same reason: the failure isn't ignorance, it's that nobody's job is to look.

What to do this week

  1. Check your SSL expiry date. Right now, with the command above. Then set an alert for 14 days out. This is the cheapest catastrophe you will ever avoid.
  2. Run PageSpeed Insights on your money page and read the field data (real users), not the lab score. The lab score is your laptop lying to you.
  3. If LCP fails, look at the hero image first. It's the hero image. It's basically always the hero image.
  4. Set width and height on every image. This is most of CLS, and it's a find-and-replace.
  5. Point your uptime monitor at the page that makes money, and have it check for a string, not a status code.
  6. curl your own robots.txt and read every line. Ask what each one is for. At least one of them will surprise you.

None of this will be the reason you grow. All of it is a reason you might suddenly stop.

Stop guessing what to do next

Connect your website and get your first growth action plan today.

No credit card required · Setup in under 2 minutes