Every 100ms of additional page load time reduces conversion rates by roughly 1%, according to Google's research. A page loading in 1 second converts 3x better than one loading in 5 seconds. Since 2021, page speed is an official ranking signal via Core Web Vitals.
Here are 15 techniques ordered by impact-to-effort ratio. Highest-value, lowest-effort first.
1. Use a CDN (Content Delivery Network)
Impact: Very High | Effort: Low
A CDN distributes your static assets across servers worldwide so users load content from the nearest location. A visitor in Tokyo downloading assets from a US server adds 150-200ms of latency. A CDN removes that entirely.
Cloudflare's free tier is excellent for most sites. Enabling it takes about 15 minutes and typically delivers 20-40% faster load times without any other changes.
2. Enable HTTP/2 or HTTP/3
Impact: High | Effort: Very Low
HTTP/2 allows multiple files to download simultaneously over a single connection (multiplexing), eliminating the HTTP/1.1 limit of 6 parallel connections. HTTP/3 adds UDP-based transport for better performance on unreliable mobile connections.
Check with our HTTP Header Checker to see which protocol your server uses. Most modern hosts support HTTP/2 by default now.
3. Optimize and Serve Images in Next-Gen Formats
Impact: Very High | Effort: Medium
Images typically account for 50-70% of total page weight. WebP is 25-35% smaller than JPEG. AVIF is 40-55% smaller with equivalent visual quality.
- Next.js: The built-in
<Image>component automatically serves WebP/AVIF - WordPress: ShortPixel, Imagify, or WebP Express
- Manual: Squoosh.app for batch conversion
4. Preload the LCP Element
Impact: Very High | Effort: Low
The LCP element (usually your hero image) is often discovered late by the browser. Preloading it tells the browser to fetch it first, cutting LCP by 0.5-1.5 seconds in most cases.
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
Add fetchpriority="high" directly to the LCP image tag as well:
<img src="/hero.webp" fetchpriority="high" loading="eager" alt="Hero">
5. Reduce Time to First Byte (TTFB)
Impact: Very High | Effort: Medium-High
TTFB is how long your server takes to send the first byte. If it's above 600ms, good LCP is nearly impossible regardless of other optimizations. Fix TTFB by:
- Using server-side caching (Redis, Varnish, or Vercel's Edge Cache)
- Moving to a faster host or plan
- Optimizing database queries — if TTFB varies by page, this is usually the cause
- Using static generation instead of server-side rendering where possible
6. Defer Non-Critical JavaScript
Impact: High | Effort: Low
JavaScript that doesn't need to run on load blocks page rendering. Add defer or async to non-critical scripts:
<!-- Blocking (bad) -->
<script src="analytics.js"></script>
<!-- Non-blocking (good) -->
<script src="analytics.js" defer></script>
For chat widgets and social buttons, load them only on user interaction or delay them 3-5 seconds after page load.
7. Implement Lazy Loading for Below-the-Fold Images
Impact: High | Effort: Very Low
Images below the fold don't need to load until the user scrolls to them. One attribute does it:
<img src="blog-thumbnail.webp" loading="lazy" alt="Article thumbnail">
Never add lazy loading to above-the-fold images or your LCP element. That's the opposite of what you want.
8. Minify and Compress CSS/JS/HTML
Impact: Medium | Effort: Very Low
Minification removes whitespace and comments. Brotli/gzip compression reduces transfer size by 60-80%.
- Vite, Webpack, and Rollup minify automatically in production builds
- Enable Brotli/gzip at the web server level
- Vercel and Netlify enable compression automatically
9. Use Font-Display: Swap for Web Fonts
Impact: Medium | Effort: Low
Custom web fonts can block text rendering until they load. font-display: swap makes text visible immediately using a system font while the custom font loads:
@font-face {
font-family: 'Syne';
src: url('/fonts/syne.woff2') format('woff2');
font-display: swap;
}
For Google Fonts, add &display=swap to the URL.
10. Preconnect to Critical Third-Party Origins
Impact: Medium | Effort: Very Low
DNS resolution and connection setup for third-party resources takes 100-300ms. Preconnecting eliminates that delay for the resources you need on every page:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
11. Set Explicit Image Dimensions
Impact: High for CLS | Effort: Low
Images without dimensions cause layout shifts (CLS) because the browser doesn't reserve space until the image loads. Always set them:
<img src="photo.webp" width="800" height="600" alt="Description">
Or use CSS aspect-ratio: aspect-ratio: 800 / 600;
12. Eliminate Render-Blocking CSS
Impact: High | Effort: Medium
CSS in the <head> blocks rendering until fully downloaded and parsed. Three approaches:
- Inline critical CSS — The styles needed for above-the-fold content only
- Load non-critical CSS asynchronously
- Remove unused CSS with PurgeCSS, which strips unused Tailwind/Bootstrap classes
13. Use Static Generation or ISR Where Possible
Impact: Very High for TTFB | Effort: Medium
SSR pages are generated on each request, adding 200-500ms. Static pages are served instantly from CDN cache. In Next.js, prefer generateStaticParams and static export for content that doesn't change per-request. The TTFB difference is dramatic.
14. Optimize Third-Party Script Loading
Impact: Very High for INP | Effort: Medium
Third-party scripts cause over 40% of INP failures. This is the most underappreciated performance problem on most sites. Practical fixes:
- Load Google Tag Manager with
strategy="lazyOnload"in Next.js - Replace heavy analytics with lighter alternatives like Plausible or Fathom
- Load chat widgets only after the user scrolls to the bottom or after 5 seconds
15. Use Resource Hints for Next Page Navigation
Impact: High for Navigation Speed | Effort: Low
If you know where users are likely to go next, prefetch or prerender those pages before they click:
<!-- Prefetch likely next page -->
<link rel="prefetch" href="/tools/keyword-density-checker">
<!-- Prerender (stronger — loads entire page in background) -->
<link rel="prerender" href="/pricing">
Next.js's <Link> component does this automatically for visible links, which is one of the reasons Next.js navigation feels so fast.
Measuring Your Improvements
Use our free Website Speed Test to benchmark before and after each optimization. Track both:
- Lab data (Lighthouse) — Immediate feedback on changes
- Field data (CrUX in Search Console) — Takes 28 days to reflect changes
The target: LCP under 2.5s, INP under 200ms, CLS under 0.1 — on mobile.