A client came to me with a WordPress site scoring 54 on Google PageSpeed Insights (mobile). Load time was 8 seconds. Core Web Vitals were all failing.
Seven days later, the same site scored 100/100 on Performance, Accessibility, Best Practices, and SEO. Load time dropped to under 2 seconds.
Here is exactly how I did it, step by step.
The Starting Point
- PageSpeed Insights mobile score: 54/100
- Largest Contentful Paint (LCP): 6.2s
- Total Blocking Time (TBT): 1,800ms
- Cumulative Layout Shift (CLS): 0.32
- Load time: 8+ seconds on a mobile connection
The site was a standard WordPress build with Elementor, 12 plugins, unoptimized images, and render-blocking CSS/JS everywhere.
Step 0: Understand the Scoring System
Before touching any code, I read the Lighthouse 10 scoring documentation. This matters because the five metrics are weighted differently:
| Metric | Weight |
|---|---|
| Total Blocking Time (TBT) | 30% |
| Largest Contentful Paint (LCP) | 25% |
| Cumulative Layout Shift (CLS) | 25% |
| First Contentful Paint (FCP) | 10% |
| Speed Index (SI) | 10% |
TBT alone is 30% of the score. That told me where to focus first.
Phase 1: Kill the JavaScript (TBT: 1,800ms to 0ms)
The biggest win. I audited every enqueued script:
function peakspire_dequeue_scripts() {
wp_deregister_script('jquery-migrate');
wp_dequeue_script('elementor-waypoints');
wp_dequeue_script('elementor-frontend');
remove_action('wp_head', 'print_emoji_detection_script', 7);
}
add_action('wp_enqueue_scripts', 'peakspire_dequeue_scripts', 100);
Result: TBT dropped from 1,800ms to 0ms. That alone jumped the score from 54 to ~75.
Phase 2: Fix the Fonts (LCP: 6.2s to 2.8s)
The site loaded three Google Font families (6 weights = ~75KB). On mobile 4G, these competed with main content for bandwidth.
The fix: load fonts only on desktop:
@media (min-width: 769px) {
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-v12-latin-regular.woff2') format('woff2');
font-display: swap;
}
}
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
Mobile uses system font stack. Zero font downloads. LCP improved dramatically.
Phase 3: Image Optimization (LCP: 2.8s to 1.8s)
- Convert to WebP: 60-70% smaller files
- Lazy load below the fold
-
Explicit width/height on every
<img>(prevents CLS) - Preload the hero (LCP element):
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" type="image/webp">
Phase 4: CSS Delivery (FCP: 3.1s to 0.8s)
Inline critical CSS, async-load the rest:
<style>
:root { --text: #1a1a2e; --bg: #ffffff; }
body { margin: 0; font-family: system-ui, sans-serif; color: var(--text); }
.hero { min-height: 60vh; display: flex; align-items: center; }
</style>
<link rel="preload" href="/style.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
FCP dropped from 3.1s to 0.8s.
The Result
| Metric | Before | After |
|---|---|---|
| Performance | 54 | 100 |
| Accessibility | 82 | 100 |
| Best Practices | 78 | 100 |
| SEO | 89 | 100 |
| LCP | 6.2s | 1.2s |
| TBT | 1,800ms | 0ms |
| CLS | 0.32 | 0 |
The Non-Obvious Lesson
After obvious fixes, the score stuck at 95-97. The breakthrough was the font strategy. Fonts only on desktop. Mobile saves 75KB on slow connections. That was the difference between 97 and 100.
I run PeakSpire in Ottawa, Canada. WordPress speed optimization for service businesses. Free speed audit: drop your URL in the comments.
Top comments (0)