WordPress Performance · 2025

How to Speed Up a Slow WordPress Site: The Complete 2025 Checklist

Updated April 2025 · 13 min read

12 fixes in priority order — from hosting migration to image optimisation to database cleanup.

HomeBlog › How to Speed Up a Slow WordPress Site: The Complete 2025 Checklist

How to Speed Up a Slow WordPress Site: The Complete 2025 Checklist

A slow WordPress site costs you in three places simultaneously: user experience, conversion rates, and Google rankings. Studies by Portent show that a site loading in 1 second has a conversion rate 3x higher than one loading in 5 seconds. Google's Core Web Vitals make page speed a direct ranking factor.

This is the complete diagnostic-to-fix checklist. Every layer is covered — from server infrastructure to image optimisation to database cleanup — in the exact order you should address them.

Step 1: Diagnose Before You Fix

Don't guess. Measure first. Most WordPress speed problems are misdiagnosed because site owners jump to caching plugins when the actual problem is something else entirely.

Run These Tests Before Touching Anything

Google PageSpeed Insights
URL: pagespeed.web.dev
What it tells you: Core Web Vitals scores (field data from real Chrome users + lab data), specific failing metrics, and actionable recommendations ordered by impact.

WebPageTest (webpagetest.org)
Test from multiple locations (Chicago, London, Frankfurt). Choose "Simple Testing" first.
What to look for:
- TTFB (Time to First Byte) — if above 500ms, your server is the problem
- Waterfall chart — shows which resources are blocking rendering
- First Contentful Paint — when does anything appear on screen?

GTmetrix
Good for identifying specific heavy resources and render-blocking scripts.

Chrome DevTools (F12 → Network tab)
Load your page with DevTools open. Look at:
- The first row (your HTML document) — its response time is your TTFB
- Large resource files (anything over 500KB)
- Render-blocking resources (CSS/JS in <head> without defer)

Interpreting Your Results

Metric Good Needs Work Critical Fix Needed
TTFB <200ms 200–500ms >500ms
LCP <2.5s 2.5–4s >4s
Total Page Size <1MB 1–3MB >3MB
HTTP Requests <50 50–80 >80
Time to Interactive <3.8s 3.8–7.3s >7.3s

If your TTFB is above 500ms, start at Fix #1 (hosting). If TTFB is fine but LCP is high, jump to Fix #5 (images) or Fix #6 (caching). Use the diagnostic to know where to begin.

Fix #1: Your Hosting Is the Root Cause (Start Here If TTFB > 500ms)

No amount of plugin-level optimisation compensates for a slow server. TTFB is determined almost entirely by your hosting infrastructure. If your server takes 1,500ms to respond, your page cannot load in under 1,500ms — no matter what else you do.

Signs Your Hosting Is the Problem

  • TTFB varies wildly across different test times (morning vs peak hours)
  • PageSpeed Insights shows "Reduce server response times (TTFB)" as a high-impact recommendation
  • Your site is fast when you test it at 2am but slow during business hours
  • You're on shared hosting with hundreds of other sites on the same server

What Causes Hosting-Level Slowness

Shared hosting puts your WordPress site on a server alongside hundreds of other accounts. During peak traffic hours, those accounts compete for the same CPU and RAM. This is called resource contention, and it causes TTFB to spike unpredictably — regardless of your WordPress configuration.

The Fix: Move to Container-Isolated Hosting

Container isolation gives your WordPress site reserved CPU and RAM. No other customer's traffic can affect your server response time.

Before moving:
- Test TTFB 10 times across different hours using: curl -o /dev/null -s -w "TTFB: %{time_starttransfer}\n" https://yoursite.com
- If results vary from 200ms to 1,500ms+, you're experiencing contention

After migrating to container-isolated hosting (e.g., ApexWeave):
- TTFB consistently 80–200ms
- Performance is stable during traffic spikes (your container isn't affected by neighbours)
- You get SSH access, Git deployment, and CLI tools

See ApexWeave WordPress hosting for container-isolated WordPress.

Fix #2: Update PHP to Version 8.3

PHP 8.3 is approximately 45% faster than PHP 7.4 for WordPress workloads. This is not a marginal improvement — it's the difference between 800ms and 440ms server processing time on an uncached page.

Check Your Current PHP Version

In WordPress admin: Tools → Site Health → Info tab → Server section

Or via WP-CLI:

wp --info | grep "PHP binary"
php -v

How to Upgrade PHP

On managed hosting (ApexWeave): Set APEXWEAVE_STACK=php:8.3 in your environment variables and redeploy. Done in 2 minutes.

On cPanel hosting: Go to cPanel → Software → PHP Version Manager → select PHP 8.3 → Apply.

Before upgrading: Check plugin and theme compatibility. Run a staging test first. Most modern plugins (post-2022) are compatible with PHP 8.2+. Issues are rare but can occur with older or abandoned plugins.

PHP performance by version (WordPress benchmark, requests/second, higher = faster):

PHP Version Requests/sec (relative) Status
PHP 7.4 100 (baseline) End of life (security patches only)
PHP 8.0 118 Deprecated
PHP 8.1 135 Active support until Dec 2025
PHP 8.2 142 Active support until Dec 2026
PHP 8.3 145 Active support until Dec 2027

PHP 7.4 reached end of life in November 2022. If you're still on it, you're running an unsupported PHP version with known security vulnerabilities — and leaving 45% performance on the table.

Fix #3: Install and Configure a Proper Caching Plugin

WordPress generates HTML dynamically — PHP executes, queries the database, builds the page, and returns the result. For a page that doesn't change between requests (a blog post, a service page), this is unnecessary work every single time someone visits.

A caching plugin stores the generated HTML as a static file. On the next request, the server returns that file directly — no PHP execution, no database queries.

TTFB comparison:
- Uncached WordPress page: 400–800ms server processing
- Cached WordPress page: 5–50ms (serving a static file)

This is the single biggest performance gain available at the plugin level.

Recommended Caching Configuration

W3 Total Cache or WP Super Cache — both are solid and free.
LiteSpeed Cache — best if your server uses LiteSpeed web server.
WP Rocket — paid ($59/year), easiest to configure correctly, best out-of-box performance.

Minimum caching configuration:
1. Enable Page Cache — caches full HTML output of pages/posts
2. Enable Browser Cache — sets proper Cache-Control headers so returning visitors load from browser cache
3. Enable Minification for CSS and JS — removes whitespace, comments, reduces file sizes 20–40%
4. Enable GZIP Compression — compresses files before sending, reduces transfer size by 70–90%

For WooCommerce sites: Never cache the Cart page, Checkout page, or My Account pages. These are dynamic (user-specific content). Cache everything else.

Object Cache (For High-Traffic Sites)

If your WordPress site has significant traffic or runs WooCommerce, add Redis or Memcached object caching. This caches database query results in memory, eliminating repeated identical queries.

Without object cache: WordPress queries the database on every page load for settings, options, and active plugins.
With object cache: First query hits the database, all subsequent queries return cached results from memory in under 1ms.

ApexWeave's managed hosting includes Redis support for object caching. Enable it by adding the Redis Object Cache plugin and connecting to your managed Redis instance.

Fix #4: Use a CDN (Content Delivery Network)

A CDN distributes your static assets (images, CSS, JavaScript, fonts) across servers in dozens of global locations. When a visitor in Tokyo requests your site hosted in the US, static assets are served from a CDN node in Tokyo — not transmitted across the Pacific.

This matters for FCP (First Contentful Paint) and LCP — especially for international audiences.

CDN Options for WordPress

Cloudflare (Free tier):
- Reverse proxy CDN — all traffic routes through Cloudflare's network
- Static asset caching, DDoS protection, SSL termination
- The free tier is genuinely capable for most WordPress sites
- Enable via: Add your site to Cloudflare → update nameservers → configure caching rules

Cloudflare setup for WordPress:
1. Create Cloudflare account → add site → change nameservers at your domain registrar
2. Go to Caching → Configuration → set Browser Cache TTL to 1 year
3. Create a Page Rule: yoursite.com/wp-admin/* → Cache Level: Bypass (never cache admin)
4. Create a Page Rule: yoursite.com/* → Cache Level: Cache Everything + Edge Cache TTL: 1 week (for static content sites)
5. Enable Rocket Loader for JavaScript optimisation

For hosting with integrated CDN (BunnyCDN, KeyCDN):
Use the CDN Enabler plugin to rewrite asset URLs to your CDN hostname. Faster implementation for non-Cloudflare CDNs.

Fix #5: Optimise Every Image on Your WordPress Site

Images are typically responsible for 50–80% of a page's total byte weight. An unoptimised hero image can be 3–8MB. The same image, properly compressed and sized, should be 80–200KB.

This directly impacts LCP — your Largest Contentful Paint is almost always an image.

Image Optimisation Checklist

1. Compress all images
Install Imagify, ShortPixel, or Smush. Run a bulk optimisation on your entire media library. Expected size reduction: 40–70% with minimal visible quality loss using lossy compression.

2. Convert to WebP format
WebP is 25–35% smaller than equivalent JPEG/PNG with the same visual quality. All modern browsers support it (Chrome, Firefox, Safari 14+, Edge).

Imagify, ShortPixel, and Smush all auto-convert to WebP and serve it via <picture> tags with JPEG fallback for old browsers.

3. Add lazy loading
Images below the fold should load only when the user scrolls near them. WordPress 5.5+ adds loading="lazy" to images automatically. Verify it's enabled:

In your theme's functions.php:

// Ensure lazy loading is active (WordPress default, but verify)
add_filter('wp_lazy_loading_enabled', '__return_true');

4. Set explicit width and height on images
Avoids layout shift (CLS) as images load. In the WordPress block editor, always fill in Width and Height fields. In HTML:

<img src="hero.webp" width="1200" height="600" alt="Description" loading="lazy">

5. Resize images to display dimensions
Don't upload a 4000×3000 pixel image to display at 800×533. WordPress creates multiple sizes on upload, but if your theme uses full-size images, they'll be served at full resolution.

Use the Force Regenerate Thumbnails plugin after updating image sizes in Settings → Media.

6. Preload your LCP image
The largest image above the fold should be preloaded — tell the browser to fetch it immediately:

<link rel="preload" as="image" href="hero.webp" fetchpriority="high">

Add this to your theme's <head>. In Elementor/Divi, this is available in performance settings.

Fix #6: Eliminate Render-Blocking JavaScript and CSS

Render-blocking resources are scripts and stylesheets that prevent the browser from rendering anything on screen until they've been downloaded and processed.

Your browser builds the DOM (the page structure) as it reads HTML top-to-bottom. When it hits a <script> or <link rel="stylesheet"> in the <head>, it stops building the DOM and waits for that file to download and process before continuing.

This directly delays FCP and LCP.

How to Fix Render-Blocking Resources

Add defer to non-critical JavaScript:

<!-- Bad: blocks rendering -->
<script src="/wp-content/plugins/some-plugin/script.js"></script>

<!-- Good: loads after HTML is parsed -->
<script src="/wp-content/plugins/some-plugin/script.js" defer></script>

Most caching plugins (WP Rocket, W3 Total Cache) have a "Delay JavaScript execution" or "Defer JS" option. Enable it, but test carefully — some scripts (particularly those that need to run before page render for visual reasons) will break if deferred.

Remove unused CSS:
WordPress loads plugin stylesheets on every page, even when the plugin's elements aren't present. A contact form plugin shouldn't load its CSS on your blog posts.

Use the Asset CleanUp plugin or WP Rocket's "Remove Unused CSS" feature (paid) to conditionally load assets only on pages where they're needed.

Inline critical CSS:
Critical CSS is the minimum CSS required to render above-the-fold content. Inline it directly in <style> tags in the <head> so it's available immediately without a network request. WP Rocket and NitroPack automate this.

Fix #7: Database Optimisation and Cleanup

WordPress writes to the database constantly — revisions, transients, spam comments, trashed posts, plugin logs. Over time, this creates database bloat that slows query times.

What Accumulates in a WordPress Database

Table What Bloats It Impact
wp_posts Post revisions (every autosave) Slows post queries
wp_options Expired transients, orphaned plugin options Slows every page load (options table loaded on init)
wp_postmeta Orphaned meta from deleted posts Slows meta queries
wp_comments Spam comments Slows comment queries

Database Cleanup with WP-CLI

# Delete all post revisions
wp post delete $(wp post list --post_type='revision' --format=ids) --force

# Delete expired transients
wp transient delete --expired

# Optimise all database tables
wp db optimize

# Check database size
wp db size --tables

Limit Post Revisions

Add to wp-config.php:

define('WP_POST_REVISIONS', 3); // Keep only last 3 revisions

Identify Slow Queries

Enable WordPress query logging to identify which queries take the most time:

// In wp-config.php — REMOVE after debugging
define('SAVEQUERIES', true);

Then in a template file:

global $wpdb;
print_r($wpdb->queries);

Any query taking over 100ms is a candidate for optimisation (adding database indexes, caching with an object cache).

Fix #8: Reduce Plugin Count and Audit Every Plugin

Every active WordPress plugin adds PHP execution overhead on every page load. Not all plugins are created equal — a poorly coded plugin with 5 database queries per page load will degrade performance more than 3 well-coded plugins combined.

How to Identify Slow Plugins

Query Monitor plugin — free, shows database queries, HTTP requests, and execution time per plugin on every page load. This is the most useful diagnostic tool for plugin-related performance.

After installing Query Monitor, load your homepage and check:
- Database Queries tab — how many queries total? Which plugins are generating the most?
- PHP Errors tab — any warnings or notices?
- Scripts / Styles tab — what assets are being loaded?

Methodology: Deactivate plugins one at a time and run a WebPageTest after each deactivation. If removing a plugin improves TTFB or load time significantly, evaluate whether that plugin's functionality justifies the cost.

Plugin Audit Checklist

For each active plugin, ask:
1. Is this actually used on this site? Deactivate and delete if not.
2. Is there a lighter-weight alternative? A full-featured contact form plugin adding 200ms overhead vs a simple AJAX form adding 10ms.
3. Does it load assets on pages where it's not used? Use Asset CleanUp to conditionally disable it.
4. When was it last updated? Plugins with no updates in 2+ years may have performance issues that have been fixed in alternatives.

Fix #9: Optimise WordPress Fonts

Google Fonts is frequently cited as a performance problem — not because the fonts themselves are slow, but because they're loaded from an external server, introducing an additional DNS lookup, TCP connection, and TLS handshake before the font can download.

Self-Host Your Google Fonts

Download fonts from google-webfonts-helper.herokuapp.com and serve them from your own domain:

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap; /* Don't block rendering waiting for font */
  src: url('/wp-content/themes/yourtheme/fonts/inter-400.woff2') format('woff2');
}

font-display: swap is critical — it tells the browser to render text in a fallback font immediately and swap to the custom font when it loads. Without it, text is invisible until the font downloads (FOIT — Flash of Invisible Text).

Limit Font Weights

Each font weight is a separate file download. Loading Inter Regular + Italic + Bold + Bold Italic = 4 network requests. Use only the weights your design actually uses.

Fix #10: Implement HTTP/2 and GZIP Compression

HTTP/2 allows multiple requests to be multiplexed over a single connection — instead of waiting for each file to download sequentially, the browser can request all assets simultaneously. This is a 30–50% load time improvement on pages with many assets.

Verify HTTP/2 is active:

curl -I --http2 https://yoursite.com 2>&1 | grep HTTP

Should return HTTP/2 200. If it returns HTTP/1.1 200, HTTP/2 is not enabled — contact your host or enable it in your server configuration.

GZIP Compression reduces file sizes by 70–90% in transit. Enable in .htaccess (Apache):

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json
</IfModule>

For Nginx (verify in your server block):

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;

Fix #11: WordPress Heartbeat API — Limit Admin Overhead

The WordPress Heartbeat API sends AJAX requests to the server every 15–60 seconds from the admin and post editor. On shared hosting, dozens of editor windows across multiple sites can generate constant server load.

Limit Heartbeat in wp-config.php or via a plugin:

// In functions.php — reduce heartbeat frequency
add_filter('heartbeat_settings', function($settings) {
  $settings['interval'] = 60; // 60 seconds instead of 15
  return $settings;
});

Or use the Heartbeat Control plugin for a no-code solution.

Fix #12: Set Up Proper Redirects (Eliminate Redirect Chains)

Every redirect adds a full HTTP round trip — typically 100–300ms per redirect. A chain of 3 redirects before reaching your actual page adds 300–900ms to TTFB before the browser even sees your HTML.

Redirect chains often accumulate from:
- Multiple CDN/SSL migrations that were never cleaned up
- www → non-www + HTTP → HTTPS combining into 2 hops instead of 1
- Old plugin-generated redirects pointing to other redirects

Check for redirect chains:

curl -L -s -o /dev/null -w "%{redirect_url}\n%{http_code}\n" https://yoursite.com

Use the Redirection plugin's 404 log to identify broken chains.

Fix: Consolidate to a single redirect. If you have HTTP → HTTPS + www → non-www, implement both in one rule:

# .htaccess — redirect HTTP + www in one hop
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^ https://yoursite.com%{REQUEST_URI} [R=301,L]

Complete WordPress Speed Optimisation Priority Order

Address these in order of impact:

Priority Fix Expected TTFB Improvement Difficulty
1 Migrate to container-isolated hosting 5–10x TTFB improvement Medium
2 Upgrade to PHP 8.3 30–45% processing speed Easy
3 Implement full-page caching 80–95% server processing reduction Easy
4 Optimise and compress all images 50–80% page weight reduction Easy
5 Add CDN (Cloudflare free) 30–60% load time for global visitors Medium
6 Remove render-blocking JS/CSS 0.5–2s FCP improvement Medium
7 Self-host Google Fonts + font-display:swap 0.2–0.8s FCP improvement Medium
8 Database cleanup and optimisation 50–200ms query reduction Easy
9 Audit and reduce plugins Variable (dependent on plugins) Medium
10 Implement HTTP/2 + GZIP 10–30% load time reduction Easy

Expected Results After Full Optimisation

A typical WordPress site on shared hosting with no optimisation:
- TTFB: 800–2,000ms
- LCP: 4–8 seconds
- Total page size: 3–8MB
- PageSpeed Insights mobile: 20–45

After applying all fixes in this guide, with container-isolated hosting:
- TTFB: 80–200ms
- LCP: 1.2–2.2 seconds
- Total page size: 300–800KB
- PageSpeed Insights mobile: 80–95

This performance range puts you comfortably in Google's "Good" Core Web Vitals range — the threshold required for the positive page experience ranking signal.

Start With Hosting If Nothing Else

If you only do one thing from this entire guide, fix your hosting.

Every other optimisation in this list is attempting to compensate for an inadequate server. With proper container-isolated hosting, a default WordPress installation with no caching plugins often performs better than a heavily optimised site on shared hosting — because the server response time is simply not the bottleneck.

Get WordPress hosting with container isolation, PHP 8.3 support, and consistent sub-200ms TTFB at apexweave.com/wordpress-hosting.php.

Get WordPress Hosting That Actually Performs

Isolated containers, git deployment, CLI management, and auto-SSL. No plugin restrictions, no visit limits.

Start WordPress Free

Powered by WHMCompleteSolution