Web Performance Optimization: Speed Up Your Site
ยท 12 min read
Core Web Vitals Explained
Core Web Vitals are Google's key metrics for measuring user experience. They directly impact your search rankings and should be the foundation of any performance optimization effort.
LCP โ Largest Contentful Paint
Target: under 2.5 seconds. LCP measures when the largest visible content element (hero image, heading, or video) finishes rendering. To improve LCP:
- Preload critical resources:
<link rel="preload" as="image" href="hero.webp"> - Use a CDN to reduce server response time (TTFB)
- Optimize and compress your largest image
- Eliminate render-blocking CSS and JavaScript
INP โ Interaction to Next Paint
Target: under 200 milliseconds. INP measures responsiveness โ how quickly the page responds to user interactions (clicks, taps, key presses). To improve INP:
- Break up long JavaScript tasks (over 50ms) into smaller chunks
- Use
requestIdleCallback()for non-critical work - Minimize main thread blocking with Web Workers
- Debounce event handlers that trigger heavy computation
CLS โ Cumulative Layout Shift
Target: under 0.1. CLS measures visual stability โ how much the page layout shifts unexpectedly during loading. To improve CLS:
- Always set
widthandheighton images and videos - Reserve space for ads and embeds with CSS
aspect-ratio - Avoid inserting content above existing content dynamically
- Use CSS
containto isolate layout changes
Image Optimization
Images typically account for 50-70% of a page's total weight. Optimizing them is the single biggest performance win for most sites.
Choose the Right Format
| Format | Best For | Compression |
|---|---|---|
| WebP | Photos, complex graphics | 25-35% smaller than JPEG |
| AVIF | Photos (best compression) | 50% smaller than JPEG |
| SVG | Icons, logos, illustrations | Scales infinitely, tiny files |
| PNG | Transparency needed, screenshots | Lossless, larger files |
Responsive Images
<!-- Serve different sizes based on viewport -->
<picture>
<source srcset="hero-800.avif 800w, hero-1200.avif 1200w, hero-1600.avif 1600w"
type="image/avif" sizes="100vw">
<source srcset="hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w"
type="image/webp" sizes="100vw">
<img src="hero-1200.jpg" alt="Hero image"
width="1200" height="600"
loading="lazy" decoding="async">
</picture>
Compression Commands
# Convert to WebP (cwebp)
cwebp -q 80 input.png -o output.webp
# Convert to AVIF (avifenc)
avifenc --min 20 --max 35 input.png output.avif
# Optimize JPEG (jpegoptim)
jpegoptim --strip-all --max=85 *.jpg
# Optimize PNG (pngquant)
pngquant --quality=65-80 --strip input.png
โก Performance & optimization tools
Lazy Loading
Lazy loading defers the loading of off-screen resources until the user scrolls near them. This dramatically reduces initial page load time.
Native Lazy Loading
<!-- Images: add loading="lazy" -->
<img src="photo.webp" alt="Description"
width="800" height="400"
loading="lazy" decoding="async">
<!-- Iframes: also supports lazy loading -->
<iframe src="https://youtube.com/embed/..." loading="lazy"></iframe>
Important: Don't lazy-load above-the-fold images (especially your LCP element). Only lazy-load images below the fold.
JavaScript Intersection Observer
// Custom lazy loading with Intersection Observer
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
observer.unobserve(img);
}
});
}, {
rootMargin: '200px' // Start loading 200px before visible
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
Code Minification
Minification removes whitespace, comments, and shortens variable names without changing functionality. It typically reduces file sizes by 30-60%.
CSS Minification
/* Before: 847 bytes */
.card {
display: flex;
flex-direction: column;
padding: 1.5rem;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
/* Card shadow for depth */
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* After minification: 156 bytes (-82%) */
.card{display:flex;flex-direction:column;padding:1.5rem;border:1px solid #e2e8f0;border-radius:.5rem;box-shadow:0 1px 3px rgba(0,0,0,.1)}
JavaScript Minification
# Using terser (recommended)
npx terser app.js -o app.min.js --compress --mangle
# Using esbuild (fastest)
npx esbuild app.js --bundle --minify --outfile=app.min.js
# Build tool integration (Vite example)
# vite.config.js
export default {
build: {
minify: 'terser', // or 'esbuild' (default, faster)
terserOptions: {
compress: { drop_console: true }
}
}
}
Use our Code Formatter during development to keep code readable, then minify for production. Our HTML Entity Encoder helps encode special characters that might break minification.
Caching Strategies
Effective caching can eliminate redundant network requests entirely, making return visits nearly instant.
HTTP Cache Headers
# Nginx caching configuration
# Static assets: cache for 1 year (immutable)
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML: short cache, always revalidate
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# API responses: no cache
location /api/ {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
Cache-Busting with Hashed Filenames
<!-- Instead of: -->
<link rel="stylesheet" href="/css/style.css">
<!-- Use hashed filenames: -->
<link rel="stylesheet" href="/css/style.a3f8b2c1.css">
<!-- Build tools (Vite, Webpack) do this automatically -->
Service Worker Caching
// sw.js - Cache-first strategy for assets
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image' ||
event.request.url.includes('/css/') ||
event.request.url.includes('/js/')) {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request).then(response => {
const clone = response.clone();
caches.open('assets-v1').then(cache => {
cache.put(event.request, clone);
});
return response;
});
})
);
}
});
CDN Configuration
A Content Delivery Network serves your content from servers closest to the user, reducing latency by 50-80% for global audiences.
Cloudflare Setup (Most Popular)
- Add your domain to Cloudflare
- Update nameservers at your registrar
- Enable these optimizations in the dashboard:
- Auto Minify โ CSS, JavaScript, HTML
- Brotli compression โ 15-20% smaller than gzip
- HTTP/3 โ faster connections
- Early Hints (103) โ preload critical resources
- Polish โ automatic image optimization
Verify your site's SSL configuration with our SSL Checker to ensure HTTPS is properly set up โ this is critical for both security and performance (HTTP/2 and HTTP/3 require HTTPS).
Preconnect and DNS Prefetch
<!-- Preconnect to critical third-party origins -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<!-- DNS prefetch for less critical resources -->
<link rel="dns-prefetch" href="https://analytics.example.com">
Critical Rendering Path
The Critical Rendering Path is the sequence of steps the browser takes to convert HTML, CSS, and JavaScript into pixels on screen. Optimizing it reduces time to first render.
Eliminate Render-Blocking Resources
<!-- Bad: blocks rendering -->
<link rel="stylesheet" href="/css/all-styles.css">
<script src="/js/app.js"></script>
<!-- Good: inline critical CSS, defer the rest -->
<style>
/* Critical above-the-fold CSS inlined here */
body { margin: 0; font-family: system-ui; }
.header { display: flex; padding: 1rem; }
</style>
<link rel="stylesheet" href="/css/all-styles.css" media="print"
onload="this.media='all'">
<!-- Defer non-critical JavaScript -->
<script src="/js/app.js" defer></script>
<script src="/js/analytics.js" async></script>
Resource Hints
<head>
<!-- Preload critical resources -->
<link rel="preload" as="font" href="/fonts/inter.woff2"
type="font/woff2" crossorigin>
<link rel="preload" as="image" href="/img/hero.webp">
<!-- Prefetch next-page resources -->
<link rel="prefetch" href="/about.html">
<!-- Prerender likely next navigation -->
<link rel="prerender" href="/pricing.html">
</head>
Performance Audit Checklist
Run through this checklist before every deployment:
- โ Images: Compressed, responsive sizes, WebP/AVIF format, width/height set
- โ Lazy loading: Below-fold images use
loading="lazy" - โ CSS: Critical CSS inlined, rest deferred, minified
- โ JavaScript: Deferred or async, code-split, tree-shaken, minified
- โ Fonts: Preloaded,
font-display: swap, subset if possible - โ Caching: Cache-Control headers set, hashed filenames for assets
- โ Compression: Brotli or gzip enabled on server
- โ CDN: Static assets served from CDN edge nodes
- โ HTTPS: SSL configured, HTTP/2 or HTTP/3 enabled
- โ Third-party scripts: Loaded async, minimal number
- โ Core Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1
- โ Lighthouse audit: Performance score > 90
Measurement Tools
- Lighthouse โ Built into Chrome DevTools (Audits tab)
- PageSpeed Insights โ
pagespeed.web.dev(real-world + lab data) - WebPageTest โ
webpagetest.org(detailed waterfall analysis) - Chrome DevTools Performance tab โ flame charts for JavaScript profiling
- Search Console โ Core Web Vitals report from real users
Frequently Asked Questions
What is a good Lighthouse performance score?
Aim for 90+ on desktop and 80+ on mobile. A score of 50-89 needs improvement, and below 50 is poor. Focus on Core Web Vitals (LCP, INP, CLS) as they have the most impact on user experience and SEO.
Does web performance affect SEO?
Yes. Google uses Core Web Vitals as ranking signals. Poor performance leads to higher bounce rates and lower engagement, which indirectly affects rankings further. Fast sites consistently outrank slow ones for similar content quality.
Should I use WebP or AVIF for images?
Use both with the <picture> element for maximum compatibility. AVIF offers the best compression (50% smaller than JPEG) but slower to encode. WebP is widely supported and 25-35% smaller than JPEG. Serve AVIF first with WebP fallback.
How do I find what's slowing down my website?
Start with Chrome DevTools: open the Performance tab, record a page load, and look at the waterfall chart. The Network tab shows which resources are largest and slowest. Run Lighthouse for specific recommendations. Use WebPageTest.org for detailed analysis including TTFB, render timing, and third-party impact.