Website Speed Optimization – Core Web Vitals Guide
Author:
Nəriman ƏsədovPublished:
April 25, 2026Why site speed decides whether visitors stay or leave
Website speed is no longer a technical curiosity discussed only by developers. It is a business metric. Google confirms that when a page takes longer than three seconds to become interactive, more than half of mobile visitors abandon it. In competitive markets such as Baku and Azerbaijan, where users compare service providers within seconds, a slow site is the fastest way to lose a lead you paid Google Ads to acquire. Search engines also translate that impatience into rankings: Core Web Vitals is an official ranking signal, meaning that a slow page will be outranked by a faster competitor even when the content is equally good.
At ONE Studio we have measured the direct impact repeatedly. One.az itself once had a Largest Contentful Paint between 8 and 13 seconds on mobile, mostly because two font providers were loaded in parallel and a heavy hero video played on the first paint. After dropping the duplicate font provider, converting the hero to a poster image, and moving critical CSS inline, LCP fell below three seconds and organic conversions rose by more than a third within a month. That is the pattern this guide will help you reproduce for your own site.
What are Core Web Vitals?
Core Web Vitals are a set of three field-measured metrics that Google uses to judge real user experience. They are collected from Chrome users through the Chrome User Experience Report (CrUX) and reported in Google Search Console under the "Core Web Vitals" section. The three metrics answer three simple questions: how fast does the page look loaded, how quickly does it react to input, and how stable is the layout while it loads.
| Metric | Target (Good) | Typical failure causes | Fix approach |
|---|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5 s | Uncompressed hero image, render-blocking CSS, slow TTFB, large web fonts | Preload the LCP image, serve AVIF/WebP, use a CDN, inline critical CSS |
| INP (Interaction to Next Paint) | < 200 ms | Long JavaScript tasks, heavy third-party scripts, hydration cost | Break long tasks, defer non-critical JS, remove unused libraries |
| CLS (Cumulative Layout Shift) | < 0.1 | Images without dimensions, injected banners, late-loading web fonts | Reserve space with width/height, use font-display: optional, avoid injecting above-the-fold ads |
The old First Input Delay (FID) metric was retired in March 2024 and replaced by INP, which measures the latency of every interaction across the whole page life, not just the first one. If your team is still tracking FID, migrate your dashboards now.
How do you measure Core Web Vitals correctly?
There are two flavours of measurement, and confusing them is the most common mistake we see. Lab data is generated by tools that load your page in a controlled environment, always at the same network speed and CPU. Field data is what real users actually experienced. Google ranks pages using field data, so lab scores of 100 mean nothing if CrUX still reports poor performance.
- PageSpeed Insights (pagespeed.web.dev) - shows both Lighthouse lab results and 28-day field CrUX data side by side. Start here.
- Chrome User Experience Report (CrUX) - the underlying dataset, queryable in BigQuery or through the CrUX API for historical trends.
- Google Search Console Core Web Vitals report - groups URLs by pattern, showing which templates need work.
- WebPageTest.org - the diagnostic microscope. Waterfall view, filmstrip, connection throttling, request-level breakdown.
- Chrome DevTools Performance panel - profile a real interaction on your own device. The Web Vitals extension shows live LCP, INP and CLS values as you browse.
Do not chase a green Lighthouse score in isolation. A page that scores 98 in Lighthouse but has a slow real-world LCP on mid-range Android phones is still a failing page in Google's eyes.
How to improve LCP?
The Largest Contentful Paint is almost always an image, a hero video poster, or a big block of text above the fold. Fix it in this order:
- Identify the element. DevTools Performance panel highlights it, and the Web Vitals extension names it. Do not guess.
- Serve it in a modern format. AVIF is 40 to 50 percent smaller than JPEG, WebP roughly 25 to 30 percent smaller. Use next/image, Laravel Media Library conversions, or a build step to generate both.
- Preload it. Add
<link rel="preload" as="image" fetchpriority="high" href="/hero.avif">so the browser starts downloading before it parses the CSS. - Deliver it from a CDN with Brotli. Cloudflare, Bunny, or Fastly cut round-trip time for users outside your origin's region. Brotli beats Gzip by 15 to 20 percent on text assets.
- Fix your Time To First Byte. If TTFB is over 800 ms, no image trick will save you. Enable full-page cache, upgrade PHP-FPM workers, or move to a modern VPS.
How to reduce INP?
INP is the metric that catches JavaScript-heavy stacks. A React or Vue application that hydrates the entire page on load will often report an INP over 300 ms on mid-range Android devices even when Lighthouse gives you 95. Practical remedies:
- Break long JavaScript tasks with
scheduler.yield()orsetTimeout(fn, 0). Any task over 50 ms is a Long Task. - Remove unused libraries. Audit your bundle with
next build --analyzeorwebpack-bundle-analyzer. Replace Moment.js with date-fns or the native Intl API. Replace Lodash with individual imports. - Defer non-critical third-party scripts. Google Tag Manager, chat widgets, and ad pixels rarely need to run on the first paint. Load them after the
loadevent, or when the user first scrolls. - Use islands or partial hydration. Astro, Qwik, and Next.js App Router with Server Components let you ship a fraction of the JavaScript a legacy SPA would require.
Image optimisation with WebP, AVIF and next/image
Images are the largest asset class on almost every website. Ninety percent of the sites we audit still serve JPEG or PNG at 1920 pixels wide to a 360-pixel-wide phone screen. Modern image handling is not optional in 2026.
- Use AVIF as the primary format with WebP as a fallback and JPEG as a last resort. All three cover 99 percent of the browser market.
- Serve responsive sources with
srcsetandsizesso a phone downloads a 480-pixel-wide file, not the desktop original. - Reserve dimensions with explicit
widthandheightattributes to prevent CLS. - Lazy-load below-the-fold images with
loading="lazy". Never lazy-load the LCP image; usefetchpriority="high"instead. - In Next.js, use the built-in
next/imagecomponent. In Laravel, use Intervention Image or spatie/laravel-medialibrary conversions.
Font strategy: fast text without invisible flashes
Fonts cause two problems: they block the LCP if they render text above the fold, and they cause CLS if they arrive after the fallback has already painted. The remedy is not to remove custom fonts but to load them predictably.
- Self-host your fonts. Third-party providers like Google Fonts add an extra DNS lookup and TCP handshake. Download the WOFF2 files, place them on your CDN, and use
@font-facedirectly. - Subset the fonts to the languages you actually serve. Latin plus Azerbaijani plus Cyrillic weighs a fraction of a full multi-script file.
- Use
font-display: swapfor body text andfont-display: optionalfor anything above the fold you want to guarantee will not shift. - Preload the primary weight:
<link rel="preload" as="font" type="font/woff2" crossorigin href="/fonts/inter-variable.woff2">. - Never load the same font from two providers. This is exactly the mistake one.az was making that pushed LCP past eight seconds.
CDN, HTTP/3 and Brotli
A CDN moves your bytes closer to the user. For a Baku-based origin serving customers across Azerbaijan, a properly warmed Cloudflare cache typically cuts TTFB in half. Combine the CDN with:
- HTTP/3 (QUIC) - fewer round trips on lossy mobile networks. Enable it in Cloudflare or on modern nginx builds.
- Brotli compression - 15 to 20 percent smaller than Gzip on HTML, CSS, JS and SVG. Set
brotli on;in nginx or enable it in Cloudflare Speed settings. - Long cache headers on hashed assets:
Cache-Control: public, max-age=31536000, immutable. Rely on filename hashing to bust the cache. - Stale-while-revalidate so the CDN can serve slightly stale HTML while it fetches a fresh copy in the background.
Reducing the JavaScript bundle
A JavaScript bundle over 300 KB gzipped is a warning sign. Over 500 KB is a crisis. Concrete steps:
- Analyse the bundle. Every stack has an analyser:
next build, Vite'srollup-plugin-visualizer, or Webpack Bundle Analyzer. - Code-split by route so users only download the JavaScript for the page they are on.
- Tree-shake unused exports. Use ES modules and named imports, never
import _ from 'lodash'. - Replace heavy dependencies. Prefer native browser APIs:
fetchover axios for simple cases,URLSearchParamsover query-string, native Intl over full i18n frameworks when you do not need translations. - Ship modern JavaScript to modern browsers with a
module/nomodulepattern.
Taming third-party scripts
Analytics, ad pixels, chat widgets, cookie banners, A/B testing scripts, error trackers: each one costs milliseconds of INP and kilobytes of bundle. Audit them ruthlessly.
- List every third-party in Chrome DevTools > Network > Domain, and challenge each one. If nobody looks at the data, remove the tag.
- Load analytics after the
loadevent, or use a server-side tagging solution. - Delay chat widgets until the user scrolls or hovers. Intercom, Tawk, and Crisp all support delayed initialisation.
- Prefer
deferoverasyncfor anything that touches the DOM, and never mix multiple analytics libraries that report the same events. - Use Google Tag Manager's built-in triggers to fire tags only where they are actually needed.
Case study: one.az from 8-13s LCP to under 3s
The one.az home page was our own worst offender. The audit revealed four compounding problems: two font providers loaded in parallel, a 4.2 MB hero video autoplaying on mobile, a render-blocking CSS bundle of 340 KB, and no CDN cache for HTML. The remediation took four working days and produced these before/after numbers on a mid-range Android device on 4G in Baku:
| Metric | Before | After |
|---|---|---|
| LCP | 8.4 - 13.1 s | 2.6 - 2.9 s |
| INP | 410 ms | 160 ms |
| CLS | 0.28 | 0.04 |
| Bounce rate (mobile) | 62 percent | 44 percent |
The single biggest win came from removing the duplicate font provider. That one change alone cut LCP by more than four seconds.
Where to go deeper
For canonical documentation, follow the sources Google itself maintains: web.dev/vitals, PageSpeed Insights, and the Chrome DevTools Performance documentation. If you want the raw field data, the CrUX BigQuery dataset is public.
Speed optimisation with ONE Studio
ONE Studio delivers Core Web Vitals audits and full optimisation programmes for Azerbaijani and international clients. Our audit produces a prioritised remediation plan, a CDN and hosting review, an image and font pipeline, and a JavaScript budget your engineering team can hold to. Client sites we ship typically settle at 90+ mobile Core Web Vitals scores and hold that score under real traffic.
Ready to make your site three to five times faster? Explore website development to build on a fast foundation, or start with a performance support and maintenance engagement to fix an existing site. Contact ONE Studio in Baku today and turn Core Web Vitals from a red warning into a competitive advantage.
Nəriman Əsədov
April 25, 2026