Google's Core Web Vitals are now ranking factors, and I see tickets every week where site owners discover their search visibility dropped because metrics stayed red. The three signals—Largest Contentful Paint, First Input Delay, and Cumulative Layout Shift—measure load speed, interactivity, and visual stability. Most hosting clients can fix poor scores without migrating servers or rewriting their stack.
Below are eight fixes that moved client sites from poor or needs-improvement ranges into good across all three metrics.
1. Optimize and serve images in modern formats
Largest Contentful Paint measures how long the biggest visible element takes to render. Hero images, banner photos, and above-the-fold product shots usually own that slot.
Serve WebP or AVIF instead of JPEG and PNG. Both formats deliver smaller file sizes at equivalent quality. Most browsers support WebP; AVIF support is growing fast. If you run WordPress, install an image optimization plugin that converts uploads on the fly or use a CDN with automatic format negotiation.
Resize images to match the display dimensions. A 3000×2000 pixel file served at 600×400 wastes bandwidth and parse time. Generate responsive image sets with srcset so the browser fetches the right size for each screen.
Compress aggressively. I've seen LCP drop two seconds just by running existing JPEGs through a tool that strips metadata and applies sane quality settings. Aim for a balance where the file is small but the image still looks sharp on a laptop screen.
<img
src="hero-800.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w"
sizes="(max-width: 600px) 100vw, 800px"
alt="Optimized hero image"
width="800"
height="450"
/>
Notice the width and height attributes. They reserve layout space and prevent shifts when the image loads.
2. Lazy-load images and iframes below the fold
Browsers waste resources loading every image at once, even those the visitor won't scroll to. Lazy loading defers offscreen images until the user approaches them.
Add loading="lazy" to any <img> or <iframe> that sits below the viewport. Modern browsers handle the rest. For older browsers, polyfills exist, but native support is widespread now.
<img src="product-photo.webp" loading="lazy" alt="Product thumbnail" width="300" height="200" />
<iframe src="https://www.youtube.com/embed/example" loading="lazy"></iframe>
Do NOT lazy-load the hero image or any element that contributes to LCP. Deferring the largest paint will make your score worse. Only apply this to content below the initial viewport.
If you use a page builder or a WordPress theme with built-in lazy loading, verify it excludes above-the-fold images. I've debugged sites where the theme lazy-loaded everything, including the banner, and LCP sat at five seconds.
3. Load fonts with font-display swap
Web fonts block text rendering by default. The browser downloads the font file, then paints the text. That delay hurts both LCP (if text is your largest element) and visual stability (if fallback fonts differ in size).
Set font-display: swap in your @font-face rules. The browser will render text in a system fallback immediately, then swap in the web font when it arrives. Users see content faster.
@font-face {
font-family: 'CustomFont';
src: url('/fonts/customfont.woff2') format('woff2');
font-display: swap;
}
Preload critical fonts to fetch them earlier in the load sequence:
<link rel="preload" href="/fonts/customfont.woff2" as="font" type="font/woff2" crossorigin />
Pick one or two weights and subsets. Loading six font files for a single typeface is overkill and delays paint. Subset fonts to include only the glyphs your site's language requires; Latin-only subsets are much smaller than full Unicode ranges.
4. Split and defer JavaScript bundles
Large JavaScript files delay interactivity and block parsing. First Input Delay measures the lag between a user's first click and the browser's response. Heavy scripts keep the main thread busy and push FID into the red.
Split your JavaScript into smaller chunks. If you use a bundler like Webpack, Rollup, or Vite, enable code splitting so each route or feature gets its own file. The browser loads what it needs for the current page, not the entire application.
Defer non-essential scripts with the defer attribute. Scripts marked defer download in parallel but execute after HTML parsing finishes. That keeps the page interactive sooner.
<script src="analytics.js" defer></script>
<script src="app.js" defer></script>
For scripts that don't manipulate the DOM or depend on page structure, use async. Those scripts execute as soon as they download, without waiting for parsing to complete. Reserve async for third-party widgets and tracking pixels that operate independently.
Minify and compress your bundles. Serve them with Brotli or Gzip compression to reduce transfer time.
5. Eliminate render-blocking CSS
Render-blocking resources—usually CSS files—pause page rendering until they download and parse. The browser refuses to paint anything until it knows the full styling rules.
Inline critical CSS directly in the <head>. Critical CSS is the subset of styles needed to render above-the-fold content. Everything else can load asynchronously. Tools exist that extract critical CSS from your stylesheets and generate an inline <style> block.
Load non-critical CSS asynchronously:
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'" />
<noscript><link rel="stylesheet" href="styles.css" /></noscript>
That snippet tells the browser to fetch styles.css without blocking render, then apply it once it arrives. The <noscript> fallback ensures users without JavaScript still get styled content.
If your site uses a CSS framework, audit what you actually need. I've seen WordPress themes ship with entire Bootstrap or Tailwind builds when the site uses ten utility classes. Remove unused rules with PurgeCSS or a similar tool.
6. Prevent cumulative layout shift with explicit dimensions
Cumulative Layout Shift measures unexpected movement of visible page elements. Images, ads, embeds, and dynamically injected content cause shifts when they load without reserved space.
Set explicit width and height attributes on every image, video, and iframe. Browsers calculate the aspect ratio and allocate space before the resource loads. That prevents the page from jumping when the content arrives.
<img src="banner.webp" width="1200" height="630" alt="Banner" />
For responsive images, the browser scales the element while preserving the aspect ratio. You don't need CSS tricks; the attributes handle it.
Reserve space for ads and embeds. If you inject ad slots dynamically, wrap them in containers with a fixed height or min-height that matches the ad dimensions. The same goes for embedded tweets, YouTube videos, or third-party widgets.
Avoid inserting content above existing content after page load. Notifications, cookie banners, and promotional strips that push the page down are common CLS culprits. Show them as overlays or reserve space at the top of the layout from the start.
Use CSS containment for dynamic regions:
.widget-container {
contain: layout;
}
Containment isolates the element so changes inside it don't trigger reflows elsewhere on the page.
7. Defer third-party scripts and use facades
Third-party scripts—analytics, chat widgets, social media embeds, advertising networks—are the worst offenders for all three Core Web Vitals. They block rendering, hog the main thread, and inject content without warning.
Defer everything that isn't immediately visible. Load analytics after the page becomes interactive. Many tag managers let you trigger scripts on user interaction instead of page load.
Use facades for heavy embeds. A facade is a static placeholder image or button that looks like the real embed. When the user clicks, you load the actual third-party script. YouTube and Vimeo embeds are prime candidates; a static thumbnail with a play button can replace a 500 KB iframe until the user opts in.
<div class="video-facade" data-video-id="example" style="background-image: url('thumbnail.jpg'); width: 560px; height: 315px; cursor: pointer;">
<button>Play Video</button>
</div>
<script>
document.querySelectorAll('.video-facade').forEach(el => {
el.addEventListener('click', () => {
const iframe = document.createElement('iframe');
iframe.src = `https://www.youtube.com/embed/${el.dataset.videoId}?autoplay=1`;
iframe.width = 560;
iframe.height = 315;
el.replaceWith(iframe);
});
});
</script>
Self-host third-party libraries when possible. Hosting Google Fonts or jQuery locally gives you control over caching and eliminates extra DNS lookups.
8. Reduce server response time (TTFB)
Time to First Byte is the gap between the browser's request and the first byte of the response. High TTFB delays everything downstream and inflates LCP. Even a fast frontend can't compensate for a slow server.
Enable caching at every layer. Use opcode caching (OPcache for PHP), object caching (Redis or Memcached), and full-page caching. Most WordPress caching plugins handle all three. If you're on shared hosting, ask support which caching methods are available.
Upgrade your hosting plan if server resources are maxed. A VPS or cloud instance with more CPU and RAM will handle concurrent requests better than an overloaded shared server. Check your resource usage in cPanel or your hosting dashboard. Sustained high CPU or memory usage means you've outgrown the plan.
Optimize database queries. Slow queries are the most common backend bottleneck I see in support. Enable query logging, identify long-running statements, and add indexes where needed. For WordPress, install a query monitor plugin to profile page load.
Use a CDN to serve static assets and cache entire pages close to your visitors. Cloudflare, BunnyCDN, and KeyCDN all integrate easily with most hosting stacks. A CDN reduces the distance between the user and the server, cutting latency.
Compress responses with Brotli or Gzip. Check your server config (Apache or Nginx) and confirm compression is active for HTML, CSS, JavaScript, and JSON. Most modern servers enable Gzip by default, but Brotli offers better ratios.
# Nginx example
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
What to measure after each fix
Run PageSpeed Insights or Lighthouse after every change. Real-world data from the Chrome User Experience Report takes weeks to update, so lab scores give you immediate feedback. Look at the three Core Web Vitals metrics and the opportunities list.
Test on a throttled connection. Desktop results often look fine while mobile scores stay poor. Use Chrome DevTools network throttling to simulate 3G or slow 4G.
Check Search Console's Core Web Vitals report. Google groups your URLs by status—good, needs improvement, poor. That report surfaces which pages are holding you back and what metric is failing.
Frequently asked questions
How long does it take for Core Web Vitals to update in Search Console?
Google aggregates field data over 28 days. After you fix issues, wait a month for the report to reflect real user measurements. Lab scores in PageSpeed Insights update immediately.
Will these fixes improve my search rankings?
Core Web Vitals are one ranking factor among hundreds. Better scores help if competing pages are otherwise equal. You'll see more noticeable gains in user experience and conversion rates than in organic traffic alone.
Do I need a developer to implement these changes?
Some fixes—like adding image dimensions and lazy loading attributes—are straightforward HTML edits. Others, like splitting JavaScript bundles or inlining critical CSS, require build tools. Most WordPress users can handle the first group with plugins; the second group benefits from developer help.
Which metric should I prioritize first?
Start with LCP. It affects the largest portion of users and usually has the most obvious fixes (images, server response time). FID is next, then CLS. In support tickets I handled, the usual path was optimizing the hero image, enabling caching, and deferring scripts—those three moves fixed most cases.
Start with images and server response
If you tackle nothing else, optimize your images and reduce TTFB. Those two changes deliver the biggest improvement for the least effort. Run a baseline test, apply the fixes above methodically, and retest. Core Web Vitals scores respond to technical changes more predictably than almost any other SEO signal.
