Google still ranks sites partly on Core Web Vitals—Largest Contentful Paint, First Input Delay, and Cumulative Layout Shift. Hosting clients email me every week asking why their scores tanked overnight or why a competitor ranks higher despite worse content. The answer is usually fixable in a day.
Below are five changes that move the needle. I've walked through each of them in cPanel environments, cloud VPS setups, and managed WordPress hosts. No theory—just the tweaks that consistently drop LCP by a second or more and flatten CLS to near zero.
1. Lazy load images and iframes the right way
Browsers now support native lazy loading. Add loading="lazy" to every <img> and <iframe> tag that sits below the fold. The browser defers fetching those assets until the user scrolls near them.
<img src="/uploads/screenshot.png" alt="Dashboard view" loading="lazy" width="800" height="450">
<iframe src="https://www.youtube.com/embed/xyz" loading="lazy"></iframe>
Always include width and height attributes. The browser reserves space before the image loads, which stops layout shift. If you skip dimensions, the page reflows when each image appears and CLS climbs.
WordPress 5.5 and newer add loading="lazy" automatically to content images. Check your theme's custom image widgets and hero sections—they often hard-code img tags without the attribute. A find-and-replace in your theme files takes ten minutes.
For older browsers that ignore the attribute, they simply load all images up front. No breakage, just no lazy behavior. That's fine—most traffic comes from evergreen browsers now.
2. Tune your CDN cache headers and compression
A CDN cuts LCP by serving static files from an edge server close to the visitor. But default CDN configs often cache assets for only an hour or skip compression on certain MIME types.
Log into your CDN dashboard (Cloudflare, KeyCDN, BunnyCDN, whatever you use) and set these:
- Browser cache TTL: 1 year for images, CSS, JS with versioned filenames (e.g.,
style.abc123.css). 1 hour for HTML. - Edge cache TTL: Match or exceed browser TTL for static assets. HTML can stay at a few minutes if you update content frequently.
- Brotli compression: Enable it. Brotli shrinks text files 15–20% more than gzip. Every CDN supports it now.
- Early Hints (HTTP 103): If available, turn it on. The CDN sends link preload headers while the origin server builds the HTML response. Cuts perceived LCP when fonts or critical CSS are preloaded.
Cloudflare's default edge cache respects your origin's Cache-Control headers. If your Apache or Nginx config sends Cache-Control: no-cache, the CDN won't cache anything. Check your origin headers:
curl -I https://yourdomain.com/style.css | grep -i cache-control
If you see no-cache or max-age=0, fix your .htaccess or Nginx config:
# Apache .htaccess
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
</IfModule>
# Nginx
location ~* \.(jpg|jpeg|png|webp|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
After you save the config, purge the CDN cache and test again. LCP should drop immediately because the browser fetches fewer bytes and reuses cached assets across page loads.
3. Convert images to WebP or AVIF
JPEG and PNG are huge compared to modern formats. A 500 KB JPEG hero image becomes 150 KB as WebP with no visible quality loss. AVIF goes even smaller—sometimes half the size of WebP—but encoding is slower and support is still rolling out.
All current browsers support WebP. AVIF works in Chrome, Edge, Firefox, and Safari 16+. Serve AVIF with a WebP fallback:
<picture>
<source srcset="/hero.avif" type="image/avif">
<source srcset="/hero.webp" type="image/webp">
<img src="/hero.jpg" alt="Homepage hero" width="1200" height="600">
</picture>
The browser picks the first format it understands and ignores the rest. No JavaScript required.
On a cPanel shared host, convert images with the cwebp or avifenc command-line tools if your host installed them. Most won't have them. Install a WordPress plugin like ShortPixel or Imagify instead—they convert on upload and serve the right format automatically. Free tiers handle a few hundred images per month.
For a VPS or dedicated server:
# Install WebP tools (Debian/Ubuntu)
sudo apt install webp
# Convert a single image
cwebp -q 85 input.jpg -o output.webp
# Batch convert a directory
for f in *.jpg; do cwebp -q 85 "$f" -o "${f%.jpg}.webp"; done
AVIF encoding needs libavif and takes longer. If you have thousands of images, queue conversions in a background job so your server doesn't lock up.
After conversion, update your HTML or let your CMS plugin rewrite image tags. Then check LCP in Chrome DevTools (Lighthouse panel). I've seen LCP drop from 4.2 seconds to 1.8 seconds just by switching a hero image to WebP.
4. Inline critical CSS and defer the rest
When a browser downloads your stylesheet, it blocks rendering until the entire file parses. A 200 KB CSS file delays FCP and LCP by hundreds of milliseconds.
Critical CSS is the subset of rules needed to render above-the-fold content. Inline it in a <style> tag in the <head>. Load the full stylesheet asynchronously:
<head>
<style>
/* Critical CSS: layout, typography, hero section */
body { margin: 0; font-family: sans-serif; }
.header { background: #333; color: #fff; padding: 1rem; }
.hero { height: 400px; background: url(/hero.webp); }
</style>
<link rel="preload" href="/style.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/style.css"></noscript>
</head>
The preload trick loads the full stylesheet without blocking rendering. Once it arrives, the onload handler swaps rel to stylesheet and the browser applies it. The <noscript> fallback serves users with JavaScript disabled.
Extracting critical CSS manually is tedious. Tools like Critical (npm package) or online generators crawl your page and output the minimal rules. WordPress plugins like WP Rocket and Autoptimize have one-click critical CSS generation.
Test after deploying: open DevTools, throttle to Fast 3G, and watch the filmstrip in the Performance panel. The page should render a meaningful layout within a second, even if images are still loading. That cuts LCP.
Don't inline more than 10–15 KB. If your critical CSS exceeds that, your stylesheet is bloated with unused rules. Run PurgeCSS or similar to strip dead code from frameworks like Bootstrap or Tailwind.
5. Preload fonts and set font-display swap
Custom fonts—Google Fonts, self-hosted WOFF2 files—delay text rendering and cause layout shift when the fallback font swaps to the web font. Two fixes:
Preload font files so the browser fetches them early:
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
The crossorigin attribute is mandatory even for same-origin fonts because font requests are always CORS-enabled.
Set font-display: swap in your @font-face rule:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: swap;
}
The browser shows fallback text immediately, then swaps to the web font when it loads. Yes, you get a brief flash of unstyled text (FOUT), but it's better than invisible text or a layout shift caused by font-display: block.
If you're loading Google Fonts, append &display=swap to the URL:
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
Google's CSS now includes font-display: swap automatically. Self-hosted fonts require you to add it.
One more thing: limit font weights and character sets. Loading Regular (400), Bold (700), and Latin-only cuts file size by 60% compared to nine weights plus Cyrillic and Greek. Most sites never use those extra glyphs.
What if your scores still haven't improved?
Run Lighthouse in Chrome DevTools (Lighthouse panel, select Mobile, Generate report). It highlights the exact bottlenecks. Common culprits I see:
- Render-blocking JavaScript: Move
<script>tags to the bottom of<body>or adddeferattribute. - Large DOM size: Pages with 3,000+ elements slow down rendering. Paginate long lists or lazy-load sections.
- Third-party scripts: Every tracking pixel and chat widget adds weight. Defer non-essential scripts with
requestIdleCallbackor load them after user interaction. - Server response time (TTFB): If your HTML takes more than 600 ms to arrive, optimize your backend. Switch to PHP 8.x with OPcache, enable Redis object caching for WordPress, or move to a faster host.
Check your hosting control panel's resource usage graphs. If CPU or I/O wait is pegged, your bottleneck is server capacity, not front-end code. Upgrade your plan or migrate to a VPS.
How long does it take to see ranking changes?
Google recrawls and re-indexes pages on different schedules. High-traffic pages get recrawled daily; low-traffic pages might wait weeks. Use Google Search Console to request re-indexing after you deploy fixes. Rankings adjust over the next crawl cycle, usually within a few days.
Do I need to optimize for all three metrics equally?
LCP is weighted most heavily in real-world ranking impact. FID is being replaced by Interaction to Next Paint (INP) in 2024+, which measures responsiveness across all interactions, not just the first. CLS matters most on mobile where layout shifts cause mis-taps. Prioritize LCP first, then CLS, then INP/FID.
Can I ignore Core Web Vitals if my content is strong?
Content quality still matters more than speed for ranking. But when two pages have similar content, the faster one wins. I've seen clients jump from page two to position three just by halving LCP. It's a tiebreaker, not the main signal, but tiebreakers decide a lot of SERPs.
Check your real-user metrics too
Lighthouse measures lab performance on a single device. Real users hit your site from different networks, devices, and locations. Open Google Search Console → Core Web Vitals report. It shows 28-day averages from actual Chrome users.
If lab scores are green but Search Console shows red, your server struggles under load or your CDN coverage has gaps in certain regions. Add more edge locations or switch CDN providers.
If both are green, you're done. Monitor them monthly—new plugins, theme updates, or ad scripts can regress scores fast.
