Google ranks sites partly on Core Web Vitals—three metrics that measure loading speed, interactivity, and visual stability. If your PageSpeed Insights report shows red or orange scores for Largest Contentful Paint, First Input Delay, or Cumulative Layout Shift, you're losing traffic.
I've worked through hundreds of performance tickets where site owners saw their rankings drop after a Core Web Vitals update. Most problems trace back to eight fixable issues. Here's how to address each one.
1. Image optimization
Images account for the majority of page weight on most sites. An uncompressed 3 MB hero image will destroy your LCP score because the browser has to download, decode, and paint it before the page feels loaded.
Start by serving images in modern formats. WebP offers 25-35% smaller file sizes than JPEG at equivalent quality, and AVIF goes further still. Your hosting stack needs to support these formats—most cPanel accounts running Apache with mod_rewrite can handle WebP through an .htaccess rule that serves WebP to compatible browsers and falls back to JPEG for older ones.
Set explicit width and height attributes on every image tag:
<img src="hero.jpg" width="1200" height="800" alt="Dashboard" />
The browser reserves the correct space before the image loads, preventing layout shifts. I've seen CLS scores drop by 0.15 just from adding dimensions.
Compress your images before upload. Tools like ImageOptim, Squoosh, or command-line utilities like jpegoptim and pngquant strip metadata and reduce quality to a visually acceptable threshold. For WordPress, a plugin like ShortPixel or Imagify automates this, but watch your server CPU usage—on shared hosting, batch-optimizing 2,000 images in one go can spike load averages and trigger suspension.
Serve responsive images using the srcset attribute:
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
width="1200"
height="800"
alt="Dashboard"
/>
Mobile users get a smaller file. Desktop users get the full resolution. Your LCP score improves because the browser downloads only what it needs.
2. Lazy loading below the fold
Lazy loading defers image and iframe requests until the user scrolls near them. This cuts initial page weight and speeds up the critical rendering path.
Modern browsers support native lazy loading:
<img src="product.jpg" loading="lazy" width="600" height="400" alt="Product" />
Apply loading="lazy" to every image that appears below the fold. Do not lazy-load your hero image or anything in the first viewport—doing so will delay LCP and hurt your score.
For iframes embedding YouTube videos or Google Maps, add the same attribute:
<iframe src="https://www.youtube.com/embed/VIDEO_ID" loading="lazy"></iframe>
If you need more control or polyfill support for older browsers, consider a JavaScript library like lazysizes. It monitors scroll position and loads images just before they enter the viewport. In my experience, native lazy loading is sufficient for most hosting environments, especially if your traffic skews toward recent Chrome, Firefox, and Safari versions.
3. Font loading strategy
Web fonts block rendering while they download. The browser either shows invisible text (FOIT) or swaps in a fallback font once the web font arrives (FOUT). Both behaviors hurt user experience and FID.
Use font-display: swap in your @font-face declarations:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
}
The browser renders text immediately in a system font, then swaps to your web font when it's ready. This prevents invisible text and keeps your page interactive.
Preload critical fonts to prioritize their download:
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin />
Only preload fonts used in above-the-fold content. Preloading every font in your stylesheet wastes bandwidth and delays other critical resources.
Host fonts locally instead of loading them from Google Fonts or Adobe Fonts. A third-party font service introduces an extra DNS lookup, TCP handshake, and TLS negotiation before the font file even starts downloading. Self-hosting eliminates that latency. Download the font files, place them in /fonts/, and reference them directly in your CSS.
4. JavaScript code splitting
Shipping a single 800 KB JavaScript bundle forces the browser to download, parse, and execute code the user might never need. This delays interactivity and inflates FID.
Split your JavaScript into smaller chunks organized by route or feature. Modern bundlers like Webpack, Rollup, and esbuild support dynamic imports:
button.addEventListener('click', async () => {
const module = await import('./feature.js');
module.init();
});
The feature code downloads only when the user clicks the button. Your main bundle stays small, the page becomes interactive faster, and FID improves.
For WordPress sites, examine which plugins load scripts on every page. A contact form plugin that enqueues 150 KB of JavaScript even on your blog archive page is a common culprit. Use the Asset CleanUp or Perfmatters plugin to disable unnecessary scripts on a per-page basis.
Minify and compress your JavaScript files. Minification strips whitespace and shortens variable names; compression (gzip or Brotli) reduces transfer size. Most hosting control panels let you enable gzip in Apache via .htaccess:
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/javascript application/javascript
</IfModule>
Brotli offers better compression ratios but requires mod_brotli and explicit configuration. Check whether your host supports it.
5. Eliminate render-blocking resources
CSS and JavaScript files in the <head> block rendering until they download. The browser won't paint anything until it processes these resources, delaying LCP.
Inline critical CSS directly in the <head>:
<style>
/* Critical styles for above-the-fold content */
body { margin: 0; font-family: sans-serif; }
.header { background: #333; color: #fff; padding: 1rem; }
</style>
Load the rest of your CSS asynchronously:
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'" />
<noscript><link rel="stylesheet" href="/styles/main.css" /></noscript>
The browser downloads main.css without blocking render. For WordPress, plugins like WP Rocket or Autoptimize generate critical CSS automatically, though results vary depending on your theme structure.
Defer non-critical JavaScript:
<script src="/js/analytics.js" defer></script>
The defer attribute tells the browser to download the script in parallel with HTML parsing and execute it after the DOM is ready. Use defer for any script that doesn't need to run immediately.
For third-party scripts that must run early (A/B testing tools, for example), consider moving them to a tag manager and loading the manager itself asynchronously. This keeps your main document lightweight.
6. Prevent cumulative layout shift
CLS measures how much visible content moves during page load. An ad that pushes down text after two seconds, or an image that loads and reflows the layout, creates a poor experience and a bad score.
Reserve space for every element that loads asynchronously. I already mentioned setting width and height on images. Do the same for ad slots:
<div class="ad-slot" style="width: 300px; height: 250px;">
<!-- Ad code here -->
</div>
If your ad network injects a 300×600 ad into a 300×250 container, you'll still get a shift, so communicate expected sizes to your ad ops team or use CSS to enforce dimensions.
Avoid inserting content above existing content unless it's in response to a user interaction. A cookie banner that slides in from the top is fine; a promotional banner that pushes the entire page down after three seconds is not.
Use CSS aspect-ratio for responsive containers:
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
The container maintains its proportions while the video loads, eliminating layout shifts.
Watch out for web fonts causing FOUT. If your fallback font has different metrics than your web font, text will reflow when the swap happens. Choose a fallback font with similar x-height and character width, or use the CSS size-adjust property to fine-tune fallback metrics.
7. Defer third-party scripts
Third-party scripts—analytics, chat widgets, social media embeds, ad networks—are a common drag on FID and LCP. Each one adds DNS lookups, requests, and JavaScript execution to your critical path.
Load third-party scripts asynchronously:
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
The async attribute downloads the script without blocking HTML parsing and executes it as soon as it's ready. Use async for scripts that don't depend on DOM structure or other scripts.
For widgets like Intercom or Drift, delay initialization until the user interacts with the page:
let chatLoaded = false;
document.addEventListener('scroll', loadChat, { once: true });
document.addEventListener('mousemove', loadChat, { once: true });
function loadChat() {
if (chatLoaded) return;
chatLoaded = true;
const script = document.createElement('script');
script.src = 'https://widget.intercom.io/widget/APP_ID';
document.body.appendChild(script);
}
The widget loads after the user scrolls or moves the mouse, keeping the initial page load fast.
Replace heavy social media embeds with static placeholders. A single embedded tweet can pull in 500 KB of JavaScript. Use a facade—a static image or preview that loads the real embed only when clicked.
So what if you have no control over a critical third-party script?
8. Reduce server response time (TTFB)
Time to First Byte measures how long the browser waits for the first byte of HTML. A slow TTFB delays everything else—LCP, FID, CLS—because nothing can render until the HTML arrives.
Enable caching at every layer. If you're running WordPress, install a page cache plugin like WP Super Cache or W3 Total Cache. These plugins generate static HTML and serve it without hitting PHP or the database. Most cPanel hosts also support Memcached or Redis for object caching, which speeds up database queries.
Optimize your database. Run OPTIMIZE TABLE on WordPress tables to defragment them:
OPTIMIZE TABLE wp_posts, wp_postmeta, wp_options;
Install the Query Monitor plugin to identify slow queries. I've found joins on large postmeta tables are a frequent bottleneck.
Use a CDN to serve static assets and cached HTML from edge locations close to your users. Cloudflare's free tier offers global CDN coverage and basic page caching. For WordPress, the Cloudflare plugin integrates automatic cache purging when you publish or update content.
Upgrade your hosting plan if your server CPU or memory is maxed out. Shared hosting accounts with 1 GB of RAM and a single CPU core struggle under moderate traffic. A VPS with 2 CPU cores and 4 GB of RAM, running LiteSpeed or Nginx with FastCGI caching, will cut TTFB by half or more in many cases.
Enable HTTP/2 or HTTP/3. HTTP/2 multiplexes requests over a single connection, reducing latency. HTTP/3 uses QUIC instead of TCP, improving performance on lossy networks. Most modern hosting control panels support HTTP/2 out of the box; HTTP/3 requires a recent version of LiteSpeed, Nginx, or Cloudflare in front of your origin.
Compress your HTML output. Gzip or Brotli compression reduces the size of your HTML document, speeding up the transfer. Check your server response headers to confirm compression is active:
curl -I -H "Accept-Encoding: gzip" https://example.com
Look for Content-Encoding: gzip in the response.
Where to start
Run PageSpeed Insights on your most-visited pages. Focus on the opportunities list—it ranks fixes by potential impact. In my experience, image optimization and render-blocking resource elimination deliver the biggest wins for the least effort. Fix those first, measure the improvement, then tackle JavaScript splitting and third-party scripts.
Core Web Vitals optimization is not a one-time project. Third-party scripts change, traffic patterns shift, and new content introduces new performance problems. Set up monitoring, check your scores regularly, and address regressions as they appear.
