How to Get 100/100 PageSpeed Score on WordPress
A step-by-step guide to achieving a perfect PageSpeed score on WordPress — based on what actually works across 30+ production sites.
To get 100/100 on PageSpeed Insights for WordPress: use fast hosting, serve WebP images, eliminate render-blocking resources, defer JavaScript, and enable full-page caching with a CDN.
Is 100/100 actually achievable?
Yes, for specific types of sites. Understanding what PageSpeed Insights measures helps set realistic targets:
PageSpeed 100 is achievable for:
- Simple brochure sites with a lightweight theme
- Blogs with minimal third-party scripts
- Landing pages built specifically for performance
PageSpeed 95+ is the realistic target for:
- WooCommerce stores (cart fragments and payment scripts add overhead)
- Sites with required third-party tools (live chat, analytics, pixel)
- Sites using page builders (though FlyingPress and critical CSS help significantly)
This guide is about getting to 100 on a clean brochure site and as close to 100 as possible on more complex builds. The techniques are the same - the ceiling just varies.
What PageSpeed Insights actually measures
Before optimising, it helps to understand what the score represents.
PageSpeed Insights runs Lighthouse against your URL and reports a weighted composite of five metrics:
| Metric | Weight | Target |
|---|---|---|
| Largest Contentful Paint (LCP) | 25% | Under 2.5s |
| Total Blocking Time (TBT) | 30% | Under 200ms |
| Cumulative Layout Shift (CLS) | 15% | Under 0.1 |
| First Contentful Paint (FCP) | 10% | Under 1.8s |
| Speed Index | 10% | Under 3.4s |
| Time to Interactive | 10% | Under 3.8s |
TBT has the highest weight. It measures how long the main thread is blocked by JavaScript execution during load. This is why third-party scripts (Google Tag Manager, Facebook Pixel, chat widgets) have such a large impact on scores.
LCP is the second-highest weight and the metric most directly affected by hosting speed and image optimisation.
Step 1: Fix server response time first
PageSpeed Insights flags server response time (TTFB) as a diagnostic. If your TTFB is above 600ms, the LCP cannot be under 2.5s - the server response alone consumes the budget.
Target: TTFB under 200ms
Achieve this by:
- Moving to a host with fast infrastructure (Cloudways Vultr HF, Kinsta, or a self-managed LiteSpeed VPS)
- Enabling page caching so most requests are served from cache at 20-40ms
- Enabling object caching (Redis or Memcached) to speed up uncached dynamic requests
Test your current TTFB: open Chrome DevTools > Network tab, load your page, click the HTML document in the waterfall, check "Time to first byte" in the Timing section.
Step 2: Choose a lightweight theme
Theme choice has more impact than any optimisation plugin. A page builder theme (Elementor, Divi, Avada) loads 300-800KB of CSS and JavaScript before any content renders. A lightweight block theme loads 20-80KB.
Recommended lightweight themes:
GeneratePress: The benchmark for WordPress performance. The free version has zero jQuery dependency, 30KB total HTML+CSS output. Premium version adds module-based features (no bloat from features you are not using). Default score on clean hosting: Lighthouse 98-100.
Kadence: Strong performance with better WooCommerce integration than GeneratePress. The block patterns library makes it fast to design without a builder.
Blocksy: Full site editing compatible with good performance. Slightly heavier than GeneratePress but more design flexibility.
Custom block theme: If you are building from scratch, a custom theme.json block theme with only the blocks you need is the lightest possible option. No theme CSS you are not using.
What to avoid for performance-critical sites:
- Elementor Hello (the base theme is fine, but Elementor's page builder loads regardless of theme)
- Divi / Extra (proprietary builder with significant CSS/JS overhead)
- Avada (one of the heaviest themes available)
- Any theme with a bundled page builder
Step 3: Configure caching correctly
Install one caching plugin and configure it completely. Running two caching plugins simultaneously causes conflicts.
Plugin choice:
- On LiteSpeed hosting: LiteSpeed Cache (free, server-level caching at 10-15ms TTFB)
- On Nginx/Apache: WP Rocket (€59/yr) or FlyingPress (€70/yr)
Critical settings:
Page caching: Enable. This is the core function - store generated HTML and serve it without PHP execution.
Browser caching: Enable with long TTLs (1 year for versioned assets, 24-48 hours for unversioned). This reduces repeat visit load times dramatically.
GZIP/Brotli compression: Enable if your host does not already compress responses. Reduces HTML, CSS, and JS transfer size by 60-80%.
CSS minification: Enable. Removes whitespace and comments from CSS files.
JavaScript minification: Enable, but test thoroughly. Aggressive minification can break scripts that rely on specific variable naming.
Delay JavaScript execution (WP Rocket) or Defer JS (LiteSpeed Cache): Enable. This is the highest-impact setting for TBT and Total Blocking Time.
Step 4: Handle the LCP image correctly
The Largest Contentful Paint is almost always the hero image on a brochure or blog site. Three settings control whether this image loads fast enough to achieve LCP under 2.5s:
Do not lazy-load the hero image:
Every caching plugin adds loading="lazy" to all images by default. The hero image should never be lazy-loaded - it is always in the viewport on initial page load, and lazy loading delays it.
Add an exclusion in your caching plugin for the hero image class or ID. Or set the attribute explicitly in your theme:
<img src="hero.webp" alt="..." width="1200" height="600" loading="eager" fetchpriority="high">
Add fetchpriority="high" to the LCP image:
This browser hint tells the browser to fetch this image at the highest priority, before other resources. It can improve LCP by 200-400ms on pages with multiple images.
Preload the LCP image:
Add a preload link in the <head>:
<link rel="preload" as="image" href="hero.webp" fetchpriority="high">
WP Rocket and LiteSpeed Cache can do this automatically when you specify the LCP image selector.
Serve the image in WebP format:
WebP images are 25-40% smaller than JPEG at equivalent quality. Convert images using Imagify, ShortPixel, or the WebP conversion built into LiteSpeed Cache.
Step 5: Eliminate render-blocking resources
Render-blocking resources are CSS and JavaScript files that prevent the browser from showing any content until they have been downloaded and processed.
Fonts:
Google Fonts makes two network requests before rendering can begin: one to fetch the CSS, one to fetch the font files. Switching to self-hosted fonts eliminates both requests.
Download your fonts from google-webfonts-helper.herokuapp.com. Host the .woff2 files on your server. Add the @font-face rules to your theme CSS:
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/outfit-regular.woff2') format('woff2');
}
The font-display: swap property is critical. Without it, text is invisible until the font loads (FOIT - Flash of Invisible Text). With swap, the browser shows a fallback font immediately and swaps to the custom font when it arrives.
Third-party scripts:
Google Tag Manager, Facebook Pixel, Hotjar, Intercom, Drift, and similar scripts are the primary cause of high TBT scores. Each of these runs significant JavaScript on the main thread during page load.
Options, in order of impact:
Remove scripts you do not need. Audit all active tags in GTM. Many sites have tags from past campaigns that are never cleaned up.
Delay loading via GTM trigger. In Google Tag Manager, create a Custom Event trigger that fires when the user first scrolls or after a 3-second timer. Route all analytics and marketing tags through this trigger instead of the All Pages trigger.
Use WP Rocket's Delay JavaScript feature. Delays all GTM execution until first user interaction.
Move to server-side tagging. Server-side GTM sends events from your server rather than the user's browser, eliminating client-side script overhead entirely. Requires more setup but removes the browser performance impact.
Step 6: Fix CLS (layout shift) issues
Cumulative Layout Shift measures visual instability - elements moving after the page loads. Common causes on WordPress sites:
Images without dimensions:
<!-- Bad: browser does not know how much space to reserve --> <img src="photo.jpg" alt="..."> <!-- Good: browser reserves exact space before image loads --> <img src="photo.jpg" alt="..." width="800" height="600">
Most WordPress themes and page builders set image dimensions correctly for standard theme images. Check custom shortcodes and block patterns.
Web fonts causing FOUT:
When the custom font loads and replaces the fallback font, text reflows can cause CLS. Mitigation: use font-display: swap AND use a system font stack as the fallback that is visually similar to your custom font.
font-family: 'Outfit', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
Dynamic banners and cookie notices:
Banners that inject above existing content after the page loads cause layout shift. Cookie consent banners are a common culprit. Ensure banners either:
- Are part of the initial HTML (not dynamically injected)
- Appear in reserved space (pre-defined height)
- Appear only at the bottom of the viewport
Real benchmark: 58 to 98 on mobile
A client brochure site (service business, 8 pages) on SiteGround Go Geek:
Before:
- PageSpeed mobile: 58
- TTFB: 840ms
- LCP: 4.2s
- TBT: 1,240ms
Changes made:
- Migrated to Cloudways Vultr HF (TTFB: 195ms)
- Replaced Elementor with GeneratePress block theme
- Installed WP Rocket with Delay JS enabled
- Converted all images to WebP via Imagify
- Added
fetchpriority="high"to hero image - Self-hosted Google Fonts (removed 2 external requests)
- Removed Hotjar and replaced with Microsoft Clarity (lighter)
- Set GTM to load on 3-second timer instead of immediately
After:
- PageSpeed mobile: 98
- TTFB: 195ms
- LCP: 1.4s
- TBT: 0ms
The 98 instead of 100 comes from the GTM tag (3ms of main thread time remains). Removing GTM entirely would achieve 100. For this client, analytics were required.
The optimisation checklist
Use this before and after each change to track progress:
Server:
- [ ] TTFB under 200ms on an uncached request
- [ ] Page caching active (verify with Chrome DevTools: look for
X-Cache: HITresponse header) - [ ] Object caching active (Redis or Memcached)
Theme:
- [ ] Lightweight theme (no page builder scripts on non-builder pages)
- [ ] No unused CSS loaded (verify via Coverage tab in Chrome DevTools)
Images:
- [ ] All images in WebP format
- [ ] All images have explicit width and height attributes
- [ ] Hero/LCP image is NOT lazy-loaded
- [ ] Hero/LCP image has
fetchpriority="high" - [ ] Hero/LCP image is preloaded in
<head>
JavaScript:
- [ ] Non-critical scripts delayed until user interaction
- [ ] Third-party script list audited (remove unused tags)
- [ ] No jQuery loaded on pages that do not need it
Fonts:
- [ ] Google Fonts replaced with self-hosted fonts
- [ ]
font-display: swapset on all@font-facerules
Layout stability:
- [ ] All images have explicit dimensions
- [ ] Cookie consent banner does not inject above content
Troubleshooting: why your score is stuck below 90
After applying the standard optimisations, some sites plateau at 82-88 and resist further improvement. The most common causes:
Third-party scripts with long main thread tasks:
In Chrome DevTools > Performance tab, look for long tasks (red blocks in the main thread track). Click each one to identify the JavaScript source. The most common offenders:
- Google Maps embed: loads ~550KB of mapping library. Replace with a static map image linked to Google Maps, or use a YouTube-style facade that loads the map only on click.
- YouTube embed: the default iframe loads 250KB of YouTube JS on page load. Use lite-youtube-embed instead - it shows a thumbnail image and loads the player only when clicked. This alone typically improves TBT by 200-400ms.
- Facebook share button: loads the entire Facebook SDK (~250KB). Replace with a simple anchor tag pointing to the Facebook share URL.
Render-blocking CSS from external services:
Some plugins add <link> tags to external CSS files (Google Fonts, Typekit, icon libraries) in the <head>. These block rendering.
Switch external font imports to a preload approach:
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Outfit" as="style" onload="this.onload=null;this.rel='stylesheet'">
Or better, self-host the fonts entirely.
Images missing explicit dimensions:
Find all images without dimensions via the DevTools console:
Array.from(document.querySelectorAll('img'))
.filter(img => !img.hasAttribute('width') || !img.hasAttribute('height'))
.map(img => img.src);
Fix each one in the theme template, or add a WordPress filter to automatically set image dimensions from the attachment metadata.
PageSpeed vs real-world performance
A 100/100 Lighthouse score does not mean the fastest experience for all real users. Lighthouse simulates a specific device: a mid-tier Android on slow 4G (1.6 Mbps, 150ms RTT). Desktop broadband users will see fast times regardless of score. Users on slower connections see worse performance than Lighthouse measures.
The practical target: pass all three Core Web Vitals in field data (Google Search Console > Experience > Core Web Vitals). Once LCP is under 2.5s, CLS under 0.1, and INP under 200ms in field data, additional Lighthouse point-chasing has no ranking benefit.
Maintaining scores over time:
Performance degrades gradually with plugin additions, GTM tags, and content changes. Set up automated monitoring (WP Umbrella monitors Lighthouse weekly) and alert when the score drops below 80. Run a full audit before and after any significant plugin or theme change.
Establish a performance budget: maximum 300KB of JavaScript, maximum 1.5MB total page weight for the homepage. Evaluate each new plugin or tracking script against these limits before installing.
- [ ] No dynamically injected banners cause layout shift
Related reading
Frequently Asked Questions
Can WordPress really get a 100 PageSpeed score?
What plugins help improve WordPress PageSpeed scores?
Does PageSpeed score affect SEO rankings?
What causes the biggest PageSpeed score drops on WordPress?
How do I eliminate render-blocking resources on WordPress?
// new_articles
Get notified when new guides drop
Practical WordPress guides from a working agency owner. No filler. Unsubscribe any time.
Was this article helpful?
Thanks for the feedback!