Web Vitals & Lighthouse Optimization Checklist for DomainIndia Sites
The three Core Web Vitals (2026)
| Metric | Good | Needs improvement | Poor | What it means |
|---|---|---|---|---|
| LCP | ≤2.5s | 2.5–4.0s | >4.0s | Time until largest visible element renders |
| INP | ≤200ms | 200–500ms | >500ms | Responsiveness to user interactions |
| CLS | ≤0.1 | 0.1–0.25 | >0.25 | Layout jumps during load |
75th percentile of real users over 28 days is what Google uses. "Works on my fast laptop" ≠ "good CWV".
Measure first
Tools
- PageSpeed Insights — enter URL, get lab + field data (pagespeed.web.dev)
- Chrome DevTools Lighthouse — local tests, simulated throttling
- Google Search Console — Core Web Vitals report (real users, aggregated)
- WebPageTest — deep filmstrip, waterfall, comparison
Run PageSpeed Insights on 5-10 key pages; log scores. Re-measure after each optimisation.
Real User Monitoring (RUM)
Lab data is misleading. Install RUM to see real user experience:
<script type="module">
import { onLCP, onINP, onCLS } from 'https://unpkg.com/web-vitals@4?module';
function sendToAnalytics({ name, value, id, rating }) {
// Your analytics endpoint, Google Analytics 4, or self-hosted Plausible
fetch('/api/vitals', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, value, id, rating, url: location.href }),
});
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
</script>Or use free services: Cloudflare Web Analytics, Plausible Analytics Vitals.
Fix LCP (Largest Contentful Paint)
LCP element is usually a hero image or above-the-fold text block.
Fix 1 — Image optimisation
- Use modern formats: WebP / AVIF (30-50% smaller than JPEG)
- Serve correctly-sized images (don't serve 4000px to a 400px viewport)
- Use
srcsetfor responsive:
<img src="hero.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w"
sizes="(max-width: 600px) 400px, 800px"
width="800" height="600"
fetchpriority="high"
alt="Hero">fetchpriority="high"on LCP image — tells browser to prioritise
On DomainIndia hosting:
- cPanel has LSCache (LiteSpeed) which auto-generates WebP — enable in LSCache → Image Optimization
- Use Cloudflare's Polish (Pro plan $25/mo) for automatic image format conversion
- Or image CDN: Cloudinary, Imgix, or self-hosted imgproxy on VPS
Fix 2 — Preload critical resources
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critical.css" as="style">
<link rel="preload" href="/hero.webp" as="image">Fix 3 — Server response time (TTFB)
Target: TTFB < 800ms. Slow server = everything slow.
- PHP: enable OPcache (see our OPcache article)
- Node.js: use clustering or worker threads
- Full-page cache at Cloudflare for static pages
- Upgrade shared → VPS if CPU is saturated
Fix 4 — Critical CSS inline
Inline above-the-fold CSS, defer the rest:
<style>
/* 5-10 KB of critical CSS for first paint */
body { font: 16px Inter; margin: 0; }
.hero { background: #0f172a; color: white; padding: 40px; }
...
</style>
<link rel="preload" href="/css/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">Tools: critical npm package, Penthouse, or PurgeCSS.
Fix INP (Interaction to Next Paint)
Replaces FID in 2024. Measures the slowest interaction (clicks, taps, keypresses).
Fix 1 — Less JavaScript
Most INP issues come from JS blocking the main thread. Audit bundles:
npx @next/bundle-analyzer
# or for Vite:
npx vite-bundle-visualizerRemove unused libraries. Use import() for code-splitting non-critical routes.
Fix 2 — Defer third-party scripts
Analytics, chat widgets, social share buttons — defer:
<script src="https://third-party.com/widget.js" defer></script>
<!-- or load after interaction: -->
<script>
setTimeout(() => {
const s = document.createElement('script');
s.src = 'https://third-party.com/widget.js';
document.body.appendChild(s);
}, 3000);
</script>Fix 3 — Break up long tasks
Long tasks (>50ms) block interactions. Use scheduler.yield() or setTimeout(0) to yield:
async function processBigList(items) {
for (let i = 0; i < items.length; i++) {
processItem(items[i]);
if (i % 50 === 0) await scheduler.yield?.() ?? new Promise(r => setTimeout(r));
}
}Fix 4 — Debounce input handlers
let timer;
input.addEventListener('input', (e) => {
clearTimeout(timer);
timer = setTimeout(() => search(e.target.value), 200);
});Fix CLS (Cumulative Layout Shift)
Content jumping around = bad CLS.
Fix 1 — Always set width/height on images
<!-- BAD -->
<img src="pic.jpg">
<!-- GOOD -->
<img src="pic.jpg" width="800" height="600" alt="">Browser reserves space before image loads. No jump.
Fix 2 — Reserve space for ads / embeds
<div style="min-height: 250px; background: #f5f5f5;" id="ad-slot"></div>Fix 3 — Font loading — avoid FOIT/FOUT
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* show fallback, swap when loaded */
}Use size-matched fallbacks to avoid CLS:
body {
font-family: 'Inter', 'Adjusted Arial', sans-serif;
/* 'Adjusted Arial' defined via @font-face with ascent/descent overrides to match Inter metrics */
}Fix 4 — Don't inject content above existing content
Adding a cookie banner on top, pushing page down = CLS. Use position: fixed for banners.
Quick wins for most DomainIndia sites
- Enable Cloudflare → orange cloud + Caching Level: Standard + Auto-minify JS/CSS/HTML
- Enable Brotli compression in nginx/Apache
- Convert hero images to WebP + set explicit width/height
- Add
loading="lazy"to below-fold images (browser-native lazy load) - Defer all JS except inline critical path
- Preload hero image + key fonts
- Inline critical CSS (first 5-10 KB)
- Upgrade PHP to 8.3+ (30-50% faster than 8.0)
- Enable OPcache with tuned settings
- Consider VPS if shared hosting TTFB > 600ms
Server-level tweaks
nginx
# gzip + brotli
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/javascript application/json image/svg+xml;
# brotli (if ngx_brotli installed)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json image/svg+xml;
# Cache static assets
location ~* .(jpg|jpeg|png|gif|webp|avif|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}Apache/.htaccess
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
</IfModule>Common pitfalls
FAQ
One of many ranking factors. Not a magic wand, but slow sites lose rankings + users. Target "Good" on all three metrics.
See our WordPress Performance Optimization article. Key: use LiteSpeed Cache on cPanel, lazy-load media, limit plugins.
Calibre, SpeedCurve (paid); Lighthouse CI on GitHub Actions (free). Run on every PR, alert on regression.
Cloudflare + orange cloud = easy 30-50% improvement. Self-host gives fine control but more work.
For image-heavy sites (e-commerce, blogs, portfolios): yes. For content-dominant: often Cloudflare polish is enough.
Faster hosting + Cloudflare = better Core Web Vitals. Explore DomainIndia hosting