🚀 PageSpeed Tips — Boost Your PageSpeed Score Above 90

Your PageSpeed Insights score is not just a number — it’s a direct reflection of real user experience. This guide takes you step by step from the easiest wins to advanced optimizations.


🎯 Understanding Your Score

Score ranges:
──────────────────────────────────────────────
  0  ──────── 49   🔴  Poor   (hurts rankings)
 50  ──────── 89   🟡  Needs improvement
 90  ─────── 100   🟢  Good   (SEO ideal)
──────────────────────────────────────────────

How the score is calculated:
  ├── LCP           25% weight
  ├── TBT           30% weight  ← Total Blocking Time
  ├── CLS           15% weight
  ├── FCP           10% weight  ← First Contentful Paint
  └── Speed Index   10% weight

📸 Phase 1 — Images (Biggest Impact)

Images are the lowest-hanging fruit for most websites and usually deliver the largest score gains.

1.1 — Convert All Images to WebP

# Before
hero.jpg     → 280 KB  ❌
product.png  → 340 KB  ❌
banner.jpg   → 180 KB  ❌

# After converting to WebP
hero.webp    →  85 KB  ✅  (-70%)
product.webp → 120 KB  ✅  (-65%)
banner.webp  →  60 KB  ✅  (-67%)

Expected score impact: +8 to +15 points

1.2 — Preload Your Hero Image

<!-- Place this in <head> as early as possible -->
<head>
  <link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />
</head>

Expected LCP improvement: -0.5 to -1.5 seconds

1.3 — Lazy Load Below-the-Fold Images

<!-- ✅ Images below the fold -->
<img src="section.webp" loading="lazy" width="800" height="400" alt="Section" />

<!-- ✅ First 2–3 images — do NOT use lazy -->
<img src="hero.webp" loading="eager" fetchpriority="high" />

Expected impact: -30% in initial request payload

1.4 — Always Declare Image Dimensions

<!-- ❌ No dimensions — causes CLS -->
<img src="product.webp" alt="Product" />

<!-- ✅ With dimensions — prevents CLS -->
<img src="product.webp" alt="Product" width="400" height="400" />

⚡ Phase 2 — JavaScript (Second Largest Impact)

2.1 — Eliminate Render-Blocking Scripts

<!-- ❌ Blocks page rendering -->
<script src="analytics.js"></script>

<!-- ✅ Non-blocking — executes after parse -->
<script src="analytics.js" defer></script>

<!-- ✅ Non-blocking — executes as soon as available -->
<script src="analytics.js" async></script>

When to use which:

defer  → Scripts that depend on the DOM (analytics, widgets)
async  → Fully independent scripts (ads, chat widgets)

2.2 — Code Splitting

// ❌ Load everything upfront
import HeavyChart from './HeavyChart';
import LargeTable from './LargeTable';

// ✅ Load on demand only
const HeavyChart = React.lazy(() => import('./HeavyChart'));
const LargeTable = React.lazy(() => import('./LargeTable'));

2.3 — Remove Unused JavaScript

Chrome DevTools → Coverage tab → identify unused code

  Common offenders:
  lodash.min.js  → 71 KB loaded, 8 KB used  (89% waste!)
  jquery.min.js  → 87 KB loaded, 12 KB used (86% waste!)

  Fix: replace with smaller alternatives or write native JS

2.4 — Break Up Long Tasks

// ❌ Blocks the main thread for 500ms+
function processLargeDataset(data) {
  data.forEach(item => heavyOperation(item));
}

// ✅ Yield to the main thread between chunks
async function processLargeDataset(data) {
  for (let i = 0; i < data.length; i++) {
    heavyOperation(data[i]);
    if (i % 50 === 0) {
      await new Promise(r => setTimeout(r, 0)); // yield
    }
  }
}

🎨 Phase 3 — CSS

3.1 — Remove Unused CSS

# PurgeCSS — automatically removes unused CSS
npx purgecss --css styles.css --content index.html --output purged/

# Real-world example:
bootstrap.css    → 197 KB → after Purge → 12 KB  ✅  (-94%)

3.2 — Critical CSS (Inline Above-the-Fold Styles)

<!-- ✅ Critical CSS inlined — renders immediately -->
<style>
  /* Only CSS needed for above-the-fold content */
  body { margin: 0; font-family: ... }
  .hero { ... }
  .nav { ... }
</style>

<!-- Rest of CSS loads non-blocking -->
<link
  rel="preload"
  href="styles.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript><link rel="stylesheet" href="styles.css" /></noscript>

3.3 — Minify CSS

styles.css        → 45 KB
styles.min.css    → 18 KB  ✅  (-60%)

Tools: cssnano, PostCSS, LightningCSS

🌐 Phase 4 — Server & Network

4.1 — Enable Brotli or GZIP Compression

Brotli outperforms GZIP by 15–20%:

  Uncompressed:   styles.css → 45 KB
  With GZIP:      styles.css → 14 KB  (-69%)
  With Brotli:    styles.css → 11 KB  (-76%) ✅

Nginx configuration:

# GZIP
gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_min_length 1000;

# Brotli (preferred)
brotli on;
brotli_types text/css application/javascript;

4.2 — Upgrade to HTTP/2 or HTTP/3

HTTP/1.1 → one request at a time (slow)
HTTP/2   → multiplexed requests simultaneously ✅
HTTP/3   → faster + QUIC protocol (no head-of-line blocking) ✅✅

Most CDNs support HTTP/3 automatically

4.3 — Cache Headers

Cache-Control: public, max-age=31536000, immutable

  Optimized images   → max-age=1 year  (never change after deploy)
  HTML pages         → max-age=0       (always revalidate)
  CSS/JS files       → max-age=1 year  + content hash versioning

4.4 — Use a CDN

Without CDN:
  User → hits single origin server → geographic latency

With CDN (Cloudflare, AWS CloudFront, etc.):
  User → hits nearest edge node → < 30ms latency ✅

Cloudflare's free plan works well for most sites.

🔤 Phase 5 — Web Fonts

<!-- ✅ Preload the primary font -->
<link
  rel="preload"
  href="/fonts/main.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

<!-- ✅ font-display: swap prevents invisible text (FOIT) -->
@font-face{ 
  font-family: 'Main Font';
  src: url('/fonts/main.woff2') format('woff2');
  font-display: swap;
 }

📊 Score Impact by Optimization

OptimizationExpected GainDifficulty
Convert images to WebP+8–15 pointsEasy ⭐
Preload LCP image+5–10 pointsEasy ⭐
Lazy loading+3–7 pointsEasy ⭐
Remove unused CSS+5–10 pointsMedium ⭐⭐
defer/async for JS+5–15 pointsMedium ⭐⭐
Enable Brotli+2–5 pointsMedium ⭐⭐
Add a CDN+5–15 pointsMedium ⭐⭐
Critical CSS+5–10 pointsHard ⭐⭐⭐
HTTP/3+3–8 pointsHard ⭐⭐⭐

🗓️ 3-Week Action Plan — From 60 to 90+

Week 1: Images (biggest impact, easiest to implement)
  ✅ Convert all images to WebP
  ✅ Preload the hero image
  ✅ Add lazy loading to below-the-fold images
  ✅ Add width/height to every image tag
  Expected gain: +15–25 points

Week 2: JavaScript
  ✅ Add defer/async to all non-critical scripts
  ✅ Remove unused third-party libraries
  ✅ Break up long tasks
  Expected gain: +8–12 points

Week 3: CSS + Server
  ✅ Remove unused CSS
  ✅ Enable Brotli compression
  ✅ Set correct Cache-Control headers
  ✅ Set up a CDN
  Expected gain: +5–10 points

Total expected: from ~60 → above 90 🟢

✅ PageSpeed Checklist

  • [ ] Run PageSpeed Insights and record your baseline score
  • [ ] Identify your LCP element
  • [ ] Convert all images to WebP (or AVIF for hero)
  • [ ] Add <link rel="preload"> for the LCP image
  • [ ] Add loading="lazy" to all below-the-fold images
  • [ ] Add width and height to every <img> tag
  • [ ] Defer or async all non-critical JavaScript
  • [ ] Remove unused CSS and JavaScript
  • [ ] Enable Brotli on your server
  • [ ] Set up a CDN
  • [ ] Re-run PageSpeed Insights and compare

🔗 Related Articles:

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top