Skip to content
Back to Blog
Performance9 min read

Improve Core Web Vitals: 7 Fixes That Work in 2026

Seven server and front-end optimizations—image compression, lazy loading, CDN config, render-blocking CSS—that measurably improve LCP, INP, and CLS scores.

Written by Abdul AbrorTechnical Hosting Support Engineer
Improve Core Web Vitals: 7 Fixes That Work in 2026
On this page

Page speed kills conversions. Every support ticket I handled about slow sites came down to a handful of fixable problems—uncompressed images, blocking scripts, misconfigured caching. Google's Core Web Vitals (Largest Contentful Paint, Interaction to Next Paint, and Cumulative Layout Shift) measure what users actually feel. Your server logs won't tell you if a hero image took four seconds to render or if a layout shift made someone click the wrong button.

These seven fixes work because they address the root causes. I've seen Lighthouse scores jump from the 40s to the 90s after applying them systematically. No magic, just deliberate tuning.

1. Compress and convert images to modern formats

Images are the single biggest payload on most pages. A 2 MB JPEG hero image will destroy your LCP score no matter how fast your server is.

Convert to WebP or AVIF. Both formats deliver the same visual quality at 30–50% smaller file sizes than JPEG. WebP has near-universal browser support now; AVIF is slightly better but Safari adoption lagged until recently. Serve WebP as the default with a JPEG fallback.

Use the <picture> element:

<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero image" width="1200" height="630">
</picture>

Always specify width and height attributes. Browsers can reserve the correct space before the image loads, which prevents layout shifts.

For batch conversion, use cwebp (part of the libwebp package on most Linux distros):

for img in *.jpg; do
  cwebp -q 85 "$img" -o "${img%.jpg}.webp"
done

WordPress plugins like ShortPixel or Imagify will automate this, but watch the server load during bulk optimization—I've seen shared hosting accounts throttled because the PHP process chewed through CPU quotas.

2. Lazy-load offscreen images and iframes

Lazy loading defers the download of images and iframes until they're about to enter the viewport. This cuts initial page weight and speeds up LCP for above-the-fold content.

Browsers support native lazy loading:

<img src="gallery-photo.jpg" loading="lazy" alt="Gallery photo" width="800" height="600">
<iframe src="https://www.youtube.com/embed/VIDEO_ID" loading="lazy"></iframe>

Don't lazy-load your hero image or anything visible on load. That delays LCP.

For WordPress, Lazy Load by WP Rocket or the native lazy loading (enabled by default since version 5.5) handles this automatically. If you're hand-coding, add loading="lazy" to every image below the fold.

3. Eliminate render-blocking CSS and JavaScript

Every external stylesheet and script in the <head> blocks rendering until it downloads and parses. Your HTML sits idle while the browser fetches and processes these files.

Inline critical CSS—the styles needed to render above-the-fold content—directly in the <head>. Load the rest asynchronously:

<head>
  <style>
    /* Critical CSS for header, hero, and initial viewport */
    body { margin: 0; font-family: sans-serif; }
    .header { background: #333; color: #fff; padding: 1rem; }
  </style>
  <link rel="preload" href="/css/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/css/main.css"></noscript>
</head>

For JavaScript, defer or async every script that isn't essential for the initial render:

<script src="analytics.js" defer></script>
<script src="non-critical.js" async></script>

Use defer when script order matters; use async when it doesn't. Both allow the HTML parser to continue without blocking.

WordPress themes often enqueue dozens of scripts. Use a plugin like Asset CleanUp or Perfmatters to disable unused scripts on a per-page basis. I routinely strip out emoji scripts, jQuery Migrate, and block library CSS on pages that don't need them.

4. Enable Gzip or Brotli compression

Text-based assets (HTML, CSS, JavaScript, JSON) should always be compressed before transmission. Gzip reduces file sizes by 60–80%; Brotli does slightly better but requires more CPU.

Most hosting control panels (cPanel, Plesk) enable Gzip by default via mod_deflate. Check your response headers:

curl -I -H "Accept-Encoding: gzip" https://yoursite.com

Look for Content-Encoding: gzip or Content-Encoding: br.

If it's missing, add this to your .htaccess (Apache):

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json
</IfModule>

For Nginx, add to your server block:

gzip on;
gzip_types text/css application/javascript application/json;
gzip_min_length 1024;

Brotli requires the ngx_brotli module (not compiled in by default). If your host supports it, enable it:

brotli on;
brotli_types text/css application/javascript;

Shared hosting often locks you out of web server config. Contact support or switch to a VPS where you control Nginx or Apache.

5. Use a CDN and tune cache headers

A CDN caches your static assets (images, CSS, JS) on edge servers close to your visitors. This cuts latency and offloads traffic from your origin server. Cloudflare's free tier works well for most sites; paid tiers add image optimization and better cache rules.

After enabling a CDN, set long cache lifetimes for static assets. Browsers and CDN nodes will store them locally and skip redundant downloads.

Add cache-control headers in your .htaccess:

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  ExpiresByType text/html "access plus 0 seconds"
</IfModule>

Nginx equivalent:

location ~* \.(webp|jpg|jpeg|png|css|js)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

HTML should have short or no cache to let dynamic content update immediately. Static assets can be cached aggressively—when you update them, change the filename or append a query string (style.css?v=2) to bust the cache.

Cloudflare's default cache rules are decent, but you can refine them under Page Rules. Set "Cache Level: Cache Everything" for static pages and "Edge Cache TTL" to control how long Cloudflare holds your content before rechecking the origin.

6. Reduce JavaScript execution time

Heavy JavaScript blocks the main thread and delays INP (Interaction to Next Paint). Users see the page but can't click or type until scripts finish running.

Audit your scripts in Chrome DevTools. Open the Performance tab, record a page load, and look at the flame chart. Long yellow blocks are JavaScript tasks hogging the main thread.

Common culprits: - Google Analytics, Tag Manager, or ad scripts loading synchronously. - jQuery and legacy plugins that run on every page. - Animation libraries that recalculate layout on scroll.

Defer third-party scripts where possible. Load Google Analytics and tag managers asynchronously or via a tag manager that supports async.

For WordPress, plugins like WP Rocket or Flying Scripts let you delay JavaScript until user interaction (click, scroll, or mouseover). This keeps the main thread free during initial load.

Minify and bundle your JavaScript. Tools like Terser (for manual builds) or WordPress plugins (Autoptimize, WP Rocket) strip whitespace and comments, reducing parse time.

7. Stabilize layout with explicit dimensions

Cumulative Layout Shift (CLS) measures how much visible content moves during page load. A hero image that loads without a reserved height will push the content below it down, causing a layout shift. Same with web fonts that swap in after the page renders.

Set explicit width and height on all images, videos, and iframes:

<img src="hero.jpg" width="1200" height="630" alt="Hero">
<iframe src="embed.html" width="560" height="315"></iframe>

For responsive images, modern browsers maintain the aspect ratio even when CSS changes the display size:

img {
  max-width: 100%;
  height: auto;
}

Use font-display: swap in your @font-face declarations to prevent invisible text while fonts load:

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap;
}

Preload critical fonts to speed up their arrival:

<link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin>

Avoid injecting content via JavaScript after the page loads. If you must, reserve space with a placeholder element.

What you'll see in Lighthouse

Run Lighthouse in Chrome DevTools (or via PageSpeed Insights) before and after applying these fixes. Performance scores are relative, but you should see measurable improvements:

  • LCP dropping from 3–4 seconds to under 2 seconds.
  • INP improving as JavaScript execution time decreases.
  • CLS falling below 0.1 (the "good" threshold) once dimensions are set.

Test on a throttled connection (Lighthouse simulates slow 4G by default) to catch issues that don't appear on fast networks. Mobile scores often lag desktop because of slower CPUs and higher latency.

Your real-world Core Web Vitals (visible in Google Search Console under the Experience section) update over 28 days based on actual user visits. Lighthouse gives you a lab snapshot; Search Console shows field data from real users. Both matter.

Can I fix Core Web Vitals without a CDN?

Yes, but you'll hit limits on high-latency routes. Compression, lazy loading, and image optimization all work without a CDN. You just won't get the edge-caching benefit. For most sites, Cloudflare's free tier is enough to see a noticeable improvement.

Do Core Web Vitals affect SEO rankings?

They're a confirmed ranking factor in Google's algorithm, but content quality and relevance still dominate. A fast page won't rank if it doesn't match search intent. That said, when two pages are otherwise equal, the faster one wins.

How do I test Core Web Vitals on a staging site?

Run Lighthouse locally in Chrome DevTools. Point it at your staging URL and run audits there. Field data (from Search Console) only appears for public sites with real traffic, so staging will only give you lab scores.

Will minifying HTML help?

Barely. HTML is already compressed via Gzip or Brotli, so removing whitespace saves a few kilobytes at most. Focus on images and JavaScript first—they're orders of magnitude larger.

Where to start

Check your images first. If your homepage loads multiple megabytes of uncompressed JPEGs, fix that before anything else. Then lazy-load offscreen content, enable compression, and audit render-blocking resources.

Run Lighthouse after each change to confirm the improvement. One fix at a time lets you isolate what worked and what didn't. Don't chase a perfect 100 score—anything above 90 is excellent, and the last few points often require diminishing returns in effort.