Have a web emergency? We'll fix your site free! →
WP Farm Guide · Speed

How to Speed Up WordPress: A Practical Guide for 2026

Step-by-step fixes for slow WordPress sites, with the numbers to aim for, the commands to run and a quick way to check that each fix worked.

  • Core Web Vitals targets
  • Caching set up in layers
  • Image, font and script fixes
  • An 18-point checklist
  • By Nicky Stafford, Founder
  • Updated October 12, 2026
  • 19 min read
Isometric planetary gearset spinning up on a dyno under a glowing 3D WordPress logo, with a speed meter sweeping to redline
In this guide: 9 chapters
  1. 01Measure first
  2. 02Find the bottleneck
  3. 03Server and hosting
  4. 04Caching layers
  5. 05Images and fonts
  6. 06CSS and JavaScript
  7. 07Database and plugins
  8. 08WooCommerce
  9. 09Keep it fast
  10. ✓Checklist
  11. ?FAQ

Most slow WordPress sites have two or three real problems. The trick is finding them before you start changing settings. A caching plugin cannot fix a slow database, and a faster server cannot fix a 4 MB hero image.

This guide works in the same order we use on client sites. You measure first, find the bottleneck, then fix the server, caching, images, scripts and database in that order. Each chapter ends with a quick test so you know the fix worked. Skip the chapters that your test results rule out.

Chapter 1

Measure first: lab data, field data and the numbers that matter

Start with numbers. A good test tells you which of the next eight chapters matter for your site, so you spend your time on the fixes that move the needle.

Lab data and field data are different

Lab data comes from one test run in a controlled setting. The 0 to 100 score in PageSpeed Insights is a lab test, and so are GTmetrix and WebPageTest reports. They are great for finding problems and checking a fix the same day.

Field data comes from real Chrome users who visited your site over the last 28 days. Google collects it in the Chrome UX Report (CrUX) and uses it for Search. PageSpeed Insights shows it at the top of the report, above the lab score, when your site has enough traffic.

Google rates a page by its 75th percentile. That means at least 3 out of 4 visits must meet the target for the page to pass. A fast test on your office Wi-Fi does not count for much if visitors on phones wait twice as long.

The targets

MetricWhat it measuresGoodNeeds workPoor
LCP (Largest Contentful Paint)How fast the main content shows up2.5 s or less2.5 to 4 sOver 4 s
INP (Interaction to Next Paint)How fast the page reacts to taps, clicks and keys200 ms or less200 to 500 msOver 500 ms
CLS (Cumulative Layout Shift)How much the layout jumps while loading0.1 or less0.1 to 0.25Over 0.25
TTFB (Time to First Byte)How fast the server starts sending the page0.8 s or less0.8 to 1.8 sOver 1.8 s

LCP, INP and CLS are the three Core Web Vitals. TTFB is a supporting number. It is not a Core Web Vital, but a slow first byte delays everything after it, so we track it too.

Take your before numbers

  1. Pick 3 to 5 key pages: the home page, your top landing page, a typical blog post, a product page and the first step of checkout if you sell online.
  2. Run each page through PageSpeed Insights on mobile. Write down the field data at the top and the lab numbers below it.
  3. If a page has no field data (common on low-traffic pages), run the lab test 3 times and keep the middle result.
  4. Open Google Search Console and check the Core Web Vitals report. It groups similar URLs, so one bad template shows up as many failing pages.
  5. Save the numbers in a sheet with the date. You will compare against them after every chapter.

You can also check TTFB from a terminal. Run it 3 times, because the first request may miss the cache.

Terminal
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s  Total: %{time_total}s\n" https://example.com/
Check it

You have LCP, INP, CLS and TTFB for each key page, on mobile, saved with today’s date. Our free speed test shows lab and field data side by side if you want a second opinion.

Chapter 2

Find the bottleneck before you fix anything

Each failing number points to a short list of causes. Use this table to decide which chapters to work on first.

What you seeMost likely causeStart with
TTFB over 0.8 sNo page cache, slow hosting, heavy plugins or a slow databaseChapters 3, 4 and 7
Slow LCP with a fast TTFBLarge hero image, image found late, render-blocking CSS or fontsChapters 5 and 6
Poor INPHeavy JavaScript, long tasks, third-party scripts, a very large pageChapter 6
CLS over 0.1Images with no size, fonts swapping, ads, embeds or banners that load lateChapter 5
Fast near your office, slow far awayNo CDN, or the CDN only caches static filesChapter 4
Slow admin, cart or logged-in pagesDatabase, missing object cache, too few PHP workersChapters 3, 7 and 8

Break LCP into its four parts

A slow LCP is always one or more of four delays. PageSpeed Insights shows this split in its LCP breakdown section.

  • Time to first byte. The browser is still waiting for the server. Fix the server and caching.
  • Load delay. The browser finds the LCP image late, often because it is a CSS background, lazy-loaded or added by JavaScript.
  • Load time. The image file is too big or comes from a slow server.
  • Render delay. The image is ready, but CSS, fonts or scripts are still blocking the page from painting.

Fix the biggest slice first. If TTFB takes 1.4 seconds of a 3 second LCP, image work alone will not get you under 2.5 seconds.

Read a waterfall

Open Chrome DevTools, go to the Network tab, check Disable cache and reload. Each row is one file. Look at what loads before your main image and how long the first row (the HTML) takes. WebPageTest shows the same view with a filmstrip, which helps when you are explaining a problem to someone else.

Check it

You can name the one or two causes behind each failing number, and you know which chapters you can skip.

Chapter 3

Fix the server: PHP, CPU, compression and location

Every request that misses the cache runs PHP and asks the database for data. Caching hides a slow server from some visitors, but logged-in users, shoppers with a cart, search pages and the admin always hit it directly.

Run a current PHP version with OPcache

WordPress recommends PHP 8.3 or newer. Each new PHP version runs WordPress faster than the last, and old versions stop getting security fixes. Check your version under Tools > Site Health > Info > Server.

  1. Copy the site to staging and switch staging to the new PHP version first.
  2. Click through the home page, forms, checkout and the admin. Then check the PHP error log for new warnings.
  3. Update any plugin or theme that throws errors, then switch the live site.

OPcache keeps compiled PHP code in memory, so the server does not have to read and compile every file on every request. Most hosts turn it on. If you manage your own server, these settings suit a typical WordPress site:

php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60
Watch out

The PHP version you see in a terminal (php -v) can differ from the one your website runs. Trust Site Health, or create a phpinfo() page, check it and delete it right away.

Give PHP enough CPU and workers

A PHP worker handles one uncached request at a time. When every worker is busy, new visitors wait in line. Signs of a worker or CPU problem are TTFB that spikes at busy hours, slow admin screens and 503 or 504 errors during sales or email campaigns. Cheap shared plans often sell the same CPU to many sites, so your speed depends on your neighbors.

Turn on HTTP/2 or HTTP/3 and Brotli

HTTP/2 and HTTP/3 let the browser download many files over one connection. Brotli and Gzip shrink text files such as HTML, CSS and JavaScript by 70% or more. Check what your site sends:

Terminal
curl -sI --http2 -H "Accept-Encoding: br, gzip" https://example.com/ | grep -iE "^HTTP|content-encoding|alt-svc"

You want to see HTTP/2 (or an alt-svc header that offers h3) and content-encoding: br or gzip. Cloudflare turns on HTTP/3 and Brotli at its edge for sites that use it.

Host close to your visitors

Distance adds delay to every round trip. Put the server in the region where most of your visitors live, then use a CDN for everyone else. A site for a Maryland law firm should not be served from Singapore.

On WP Farm

Every plan runs PHP 8.3 with OPcache on NGINX, with dedicated CPU power and HTTP/3 plus Brotli through Cloudflare. We size PHP workers for each site’s traffic.

Check it

Force a cache miss by adding a unique query string, such as https://example.com/?nocache=12345, then run the curl TTFB test from Chapter 1. An uncached page should start arriving in under 0.8 seconds.

Chapter 4

Set up caching in layers

Caching saves work so it does not have to be done again. A fast WordPress site uses four layers, and each one helps a different kind of visit.

  1. 1
    Browser cacheOn the visitor’s device
    Keeps images, CSS and JavaScript after the first visit, so repeat page views download almost nothing.
  2. 2
    Edge cache (CDN)At CDN locations near the visitor
    Serves static files, and with a cache rule full HTML pages, from a server close to the visitor. Absorbs traffic spikes.
  3. 3
    Page cacheOn your web server
    Saves the finished HTML of each page, so logged-out visitors skip PHP and the database.
  4. 4
    Object cache (Redis)In server memory
    Keeps the results of repeat database lookups. Helps logged-in users, carts, search and the admin, where page caching cannot.
  5. DB
    PHP and the databaseThe slow path
    Runs only when every layer above misses. The goal is to reach it as rarely as possible.

A request stops at the first layer that has a fresh copy.

Browser cache: long lifetimes for static files

WordPress adds a version to CSS and JavaScript file names (style.css?ver=6.8), so a new version gets a new URL. That makes it safe to tell browsers to keep these files for a year. On Apache, add this to .htaccess:

.htaccess (Apache)
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/avif "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType font/woff2 "access plus 1 year"
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
</IfModule>

Page cache: one plugin or one server cache

Use one page cache only. Two caching plugins, or a plugin on top of a host cache that does the same job, fight each other and cause stale or broken pages. If you remove a caching plugin, check that it did not leave wp-content/advanced-cache.php behind or WP_CACHE set in wp-config.php. We find leftover files like these on sites moving from other hosts all the time, and they quietly block the new cache.

Never cache pages that change per visitor. Every page cache and CDN rule should skip these:

  • Paths: /wp-admin/, /wp-login.php, /cart/, /checkout/, /my-account/ and any membership or account pages.
  • Cookies: wordpress_logged_in_, wp-postpass_, comment_author_, woocommerce_items_in_cart, woocommerce_cart_hash and wp_woocommerce_session_.
  • Requests with a POST method and responses that send Cache-Control: no-store or private.

Object cache: Redis

Redis keeps database results in memory. It needs a Redis server from your host, plus a plugin that connects WordPress to it. With WP-CLI:

Terminal
wp plugin install redis-cache --activate
wp redis enable
wp redis status

If several sites share one Redis server, give each site its own prefix in wp-config.php so their data never mixes:

wp-config.php
define( 'WP_REDIS_PREFIX', 'example_com_' );

Edge cache: CDN for files, and for HTML when it is safe

A CDN copies your files to servers around the world. Most CDNs cache images, CSS and JavaScript out of the box. Caching the HTML page itself at the edge cuts TTFB for visitors everywhere, but it needs a rule that skips the paths and cookies listed above. In Cloudflare, create a Cache Rule set to Eligible for cache with an expression like this:

Cloudflare Cache Rule expression
(http.host eq "example.com"
 and not starts_with(http.request.uri.path, "/wp-admin")
 and not starts_with(http.request.uri.path, "/wp-login.php")
 and not starts_with(http.request.uri.path, "/cart")
 and not starts_with(http.request.uri.path, "/checkout")
 and not starts_with(http.request.uri.path, "/my-account")
 and not http.cookie contains "wordpress_logged_in"
 and not http.cookie contains "woocommerce_items_in_cart"
 and not http.cookie contains "wp_woocommerce_session")
Watch out

Set Edge TTL to use the cache-control header from your server (the API calls this respect_origin). We learned this one the hard way: with a forced edge lifetime, a security challenge page sent with no-store got cached and shown to every visitor in one region. Respecting the origin keeps pages marked no-store out of the cache.

On WP Farm

Every plan includes a server page cache and Redis object cache. Our Cloudflare CDN Basic tier and up adds full-page edge caching, with carts, checkout and logged-in visitors kept out of the cache automatically.

Check it

Request a page twice and read the headers:

curl -sI https://example.com/ | grep -iE "cf-cache-status|x-cache|age|cache-control"

The second request should show HIT. Your cart, checkout and admin pages should show BYPASS, DYNAMIC or MISS every time.

Chapter 5

Fix images and fonts

Images are usually the heaviest part of a page and often the LCP element. Fonts are small, but they can hide your text or make the layout jump. Both are quick wins.

Images

  1. Resize to the size shown. A 4000 px photo in a 1200 px slot wastes most of its bytes. When you add images through the WordPress editor, WordPress creates smaller copies and a srcset so phones get a smaller file. Images added as page builder backgrounds or in custom HTML often skip this.
  2. Use WebP or AVIF. Both are much smaller than JPEG and PNG at the same quality. WordPress has supported WebP since version 5.8 and AVIF since 6.5, as long as your server’s image library supports them. As a rough guide, keep a hero image under 200 KB.
  3. Set width and height. When the browser knows an image’s size, it saves the space and the layout does not jump. WordPress adds these to editor images. Check theme and builder images by hand.
  4. Lazy-load images below the fold. WordPress adds loading="lazy" for you and skips the first large image. Never lazy-load the LCP image. It delays the most important picture on the page.
  5. Give the LCP image priority. WordPress 6.3 and newer adds fetchpriority="high" to the image it thinks is the LCP. Sliders and page builders can fool it, so check the result in the page source.

A hero image done right looks like this:

HTML
<img src="/wp-content/uploads/hero-1200.webp"
     srcset="/wp-content/uploads/hero-800.webp 800w,
             /wp-content/uploads/hero-1200.webp 1200w"
     sizes="(max-width: 800px) 100vw, 1200px"
     width="1200" height="675"
     alt="Our team at work"
     fetchpriority="high">

If the hero is a CSS background image, the browser cannot find it until the CSS loads. Preload it in the page head, or switch it to a normal image:

HTML (in <head>)
<link rel="preload" as="image" href="/wp-content/uploads/hero-1200.webp" fetchpriority="high">

Hero sliders are a common LCP problem. They load several large images and often wait for JavaScript before showing the first one. A single static hero is almost always faster.

Fonts

  1. Host fonts on your own server. Download them, or turn on the “load fonts locally” option in your theme or page builder. This removes a connection to another company’s server on every first visit.
  2. Load less. Use one or two families and only the weights you use. Use WOFF2 files only.
  3. Show text right away. font-display: swap shows a fallback font while the web font loads.
  4. Preload the main font file. Preload the one font used above the fold, usually your body or heading weight.
CSS
@font-face {
  font-family: "DM Sans";
  src: url("/wp-content/uploads/fonts/dm-sans-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
HTML (in <head>)
<link rel="preload" href="/wp-content/uploads/fonts/dm-sans-500.woff2" as="font" type="font/woff2" crossorigin>

The crossorigin attribute is required on font preloads, even for your own domain. Without it, the browser downloads the font twice.

On WP Farm

On Cloudflare CDN Basic and up, our plugin hosts fonts locally and handles lazy loading. Premium and up adds cloud image optimization that converts and resizes images for you.

Check it

In PageSpeed Insights, the image delivery and font checks come back clean, the LCP element is your hero image, and CLS is 0.1 or less.

Chapter 6

Tame CSS and JavaScript

The browser must download and read CSS before it paints the page, and JavaScript keeps the browser busy while it runs. Long tasks (anything over 50 milliseconds) make the page slow to react, which is what INP measures.

CSS

  • Remove unused CSS. Themes and page builders ship styles for every widget they offer. Turn off features you do not use, and use your optimizer’s “remove unused CSS” option if it has one.
  • Inline critical CSS. Put the small amount of CSS needed for the top of the page in the HTML, then load the rest without blocking. Most optimization plugins can do this.
  • Minify CSS, JavaScript and HTML. Minifying strips spaces and comments. The gain is small on top of Brotli, but it is free.

Defer and delay JavaScript

Defer downloads a script in the background and runs it after the HTML is read, in order. It is safe for most scripts. Delay waits until the visitor scrolls, taps or presses a key. It suits chat widgets, heatmaps, extra analytics and popups. It does not suit menus, sliders or anything visible at the top of the page.

If you write your own theme code, WordPress 6.3 and newer can add defer for you:

functions.php
wp_enqueue_script(
  'my-script',
  get_stylesheet_directory_uri() . '/js/my-script.js',
  array(),
  '1.0',
  array( 'strategy' => 'defer', 'in_footer' => true )
);
Watch out

When we rolled our optimizer out to more than 200 sites, three kinds of script broke most often:

1) Settings that WordPress prints before a script (handles ending in -js-extra or -js-before) were deferred while the script that needs them ran right away. 2) Code waiting on jQuery’s ready event ran before later deferred files loaded. 3) Old themes calling $(window).load() broke once jQuery Migrate was removed.

Test menus, forms, sliders, popups and checkout after every change to script loading.

Cut third-party scripts

Chat widgets, tag managers, heatmaps, ad pixels and embedded videos often cost more than WordPress itself. List every outside script on your key pages, ask who uses each one, and remove the ones nobody checks. For the rest:

  • Delay them until the first interaction.
  • Replace YouTube and map embeds with a click-to-load image (often called a facade).
  • Load scripts on the pages that need them only, such as a booking widget on the booking page.
  • Add preconnect for an outside domain you must use early, so the connection is ready when it is needed:
HTML (in <head>)
<link rel="preconnect" href="https://www.googletagmanager.com">

Fix INP and page size

Poor INP means the page takes too long to react. Record a slow interaction in the DevTools Performance panel and look for long tasks in red. Common causes are mega menus, sliders, analytics that run on every click and very large pages.

Lighthouse warns when a page has more than about 800 HTML elements and flags it as excessive at about 1,400. Page builders add a lot of wrappers. In Elementor, use Flexbox containers instead of nested sections and columns, keep nesting shallow and turn on the options under Elementor > Settings > Performance. Elements you hide on mobile still load, so build separate mobile sections only when you have to.

On WP Farm

Our plugin defers and delays scripts based on your CDN tier, keeps WordPress settings scripts in the right order and runs a safer profile on stores, so checkout and tracking keep working.

Check it

Lab Total Blocking Time is under 200 ms, the browser console shows no new errors, and you have clicked through the menu, forms, popups and checkout on a phone.

Chapter 7

Clean the database and audit plugins

Plugins leave data behind, revisions pile up and scheduled tasks fail quietly. A heavy database slows every uncached page and the whole admin. These steps use WP-CLI. Back up the database before you run any of them.

Terminal
wp db export before-cleanup.sql

Trim autoloaded options

Autoloaded options load on every single request, cached or not. Site Health in WordPress 6.6 and newer warns when they add up to more than 800 KB. Check your total:

Terminal
wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024) AS autoload_kb FROM $(wp db prefix)options WHERE autoload IN ('yes','on','auto','auto-on');"

Then list the biggest ones:

Terminal
wp db query "SELECT option_name, ROUND(LENGTH(option_value)/1024) AS kb FROM $(wp db prefix)options WHERE autoload IN ('yes','on','auto','auto-on') ORDER BY LENGTH(option_value) DESC LIMIT 20;"

Options named after plugins you deleted long ago are safe to remove with wp option delete option_name. If you are not sure what an option does, leave it.

Limit revisions and clear expired transients

wp-config.php
define( 'WP_POST_REVISIONS', 10 );
Terminal
# Delete expired transients
wp transient delete --expired

# Optimize the tables
wp db optimize

Check Action Scheduler and WP-Cron

WooCommerce and many other plugins queue background jobs in Action Scheduler. Completed jobs are kept for 31 days by default. Thousands of failed or past-due jobs usually mean scheduled tasks are not running.

Terminal
wp db query "SELECT status, COUNT(*) FROM $(wp db prefix)actionscheduler_actions GROUP BY status;"

To keep a busy store’s table smaller, shorten how long finished jobs are kept:

functions.php or a code snippet
add_filter( 'action_scheduler_retention_period', function () {
  return 7 * DAY_IN_SECONDS;
} );

WP-Cron runs scheduled tasks during visitor page loads. On a busy site that slows random visitors, and on a quiet site tasks run late. Turn it off and run it from a real server schedule instead:

wp-config.php
define( 'DISABLE_WP_CRON', true );
crontab
*/5 * * * * cd /home/user/public_html && wp cron event run --due-now > /dev/null 2>&1

Audit plugins by weight

There is no magic plugin count. One heavy plugin can cost more than twenty light ones. Install the free Query Monitor plugin, load a slow page while logged in and check:

  • Queries by Component: which plugin runs the most database queries and the slowest ones.
  • HTTP API Calls: plugins that call outside services before the page can finish.
  • Hooks and actions: plugins that load on pages where they are not used.

Remove plugins nobody uses, replace heavy ones with lighter options and test on staging. The Health Check & Troubleshooting plugin can turn plugins off for your session only, so you can test without affecting visitors.

On WP Farm

Our Speed Optimization service includes this cleanup, a plugin audit and moving WP-Cron to a server schedule. Our plugin auditing page covers what we look for.

Check it

Autoloaded options are under 800 KB, Action Scheduler shows no pile of failed or past-due jobs, and Query Monitor shows no single plugin taking more than a few hundred milliseconds on a normal page.

Chapter 8

Speed up WooCommerce stores

Stores are harder to speed up because the pages that matter most, the cart and checkout, can never be cached. Server speed, Redis and lean scripts matter more for a store than for a blog.

Cart fragments

Cart fragments is the script that keeps the mini-cart count up to date. Each page view sends an uncached request to ?wc-ajax=get_refreshed_fragments. Since WooCommerce 7.8, the script only loads where the Cart widget is shown, but many themes still put that widget in the header of every page.

  1. Open DevTools > Network on a product page and filter for wc-ajax. If get_refreshed_fragments runs on every page, your theme loads it everywhere.
  2. Switch to the Mini-Cart block if your theme supports it, or limit the header cart to visitors who have something in their cart.
  3. Retest add to cart, the cart count and checkout after the change.

Turn on High-Performance Order Storage (HPOS)

HPOS stores orders in their own database tables instead of the shared posts tables, which makes order screens and checkout faster on busy stores. It is the default for new stores since WooCommerce 8.2. Older stores have to switch on their own:

  1. Go to WooCommerce > Settings > Advanced > Features.
  2. Check that every order-related plugin is marked compatible. WooCommerce lists the ones that are not.
  3. Turn on compatibility mode so both storage types stay in sync, and let it finish.
  4. Switch to High-performance order storage, test orders, refunds and reports, then turn compatibility mode off after a few days.

Keep the store dynamic in the right places

  • Exclude cart, checkout and account pages, plus the WooCommerce cookies from Chapter 4, from every cache.
  • Use Redis. Logged-in customers and carts get most of their speed from the object cache.
  • Show a sensible number of products per page. 24 is plenty for most shops.
  • Use the Action Scheduler check from Chapter 7. Stores pile up the most background jobs.
  • Run a safer script profile on stores. Payment, tax and tracking scripts break more easily when deferred.
On WP Farm

Stores run a safe optimization profile by default. Cart, checkout and account pages are never cached, and every plan includes Redis.

Check it

As a logged-out visitor, add a product to the cart and confirm the count updates on the next page. Then check that cart and checkout are never served from cache:

curl -sI https://example.com/checkout/ | grep -i cf-cache-status should show DYNAMIC or BYPASS.

Chapter 9

Keep it fast after the work is done

Sites slow down over time. New plugins, new tracking scripts, bigger images and theme updates add weight a little at a time. A short routine keeps the gains.

  • Monthly: check the Core Web Vitals report in Search Console and the field data in PageSpeed Insights. Field data covers 28 days, so changes show up slowly.
  • After every update or new plugin: run the lab test on your key pages the same day and compare with your saved numbers.
  • Before adding a script: ask who will use it and what it costs. Agree on a budget, for example LCP under 2.5 seconds on mobile for every key page.
  • Keep a change log. A one-line note with the date for each change makes it easy to trace a slowdown later.
  • Use staging for big changes. Test theme switches, builder upgrades and new plugins on a copy first.
  • Watch uptime and response time. A monitor that checks your site every few minutes catches slowdowns and outages before visitors report them. Our uptime monitor page explains how ours works.
On WP Farm

Every plan includes Core Web Vitals tracking in your dashboard, weekly plugin and core updates, uptime monitoring and ongoing re-tuning by our team.

Check it

You have a monthly reminder, a saved baseline and a change log. When a number gets worse, you can match it to a date and a change.

Written and reviewed by Nicky Stafford, founder of WP Farm. WP Farm has run managed WordPress hosting since 2008. Every step in this guide is one our team uses on the sites we host, including a rollout of our own caching and optimization plugin to more than 200 live sites in 2026.

The 18-point speed checklist

The same 18 checks our free speed test looks for. Tick them off as you go, or print the list for your developer.

  • Ch. 4
  • Ch. 4
  • Ch. 6
  • Ch. 6
  • Ch. 5
  • Ch. 5
  • Ch. 5
  • Ch. 5
  • Ch. 3
  • Ch. 6
  • Ch. 6
  • Ch. 5
  • Ch. 7
  • Ch. 4
  • Ch. 6
  • Ch. 4
  • Ch. 6
  • Ch. 3

Each item links to the chapter that explains it. Checkmarks are not saved, so print the list if you want to keep it.

Rather hand it off?

Everything in this guide is part of our Speed Optimization service. Pay once per site, or host with us and keep it tuned every month.

Speed Optimization

One-time fix

A full hands-on speed tune-up of one WordPress site, tested on staging first.

How many sites?

$200$400$600$800$900one-timeone-time, $200 per siteone-time, $200 per siteone-time, $200 per siteone-time, $180 per site
  • Core Web Vitals diagnostic
  • Page cache and Cloudflare tuning
  • Image and font fixes
  • Script defer and delay, tested
  • Database cleanup and plugin audit
  • Redis object cache setup
  • PageSpeed before and after report
  • 1 month of free WP Farm hosting

No commitment required. Includes the Fast or It’s Free Guarantee.

Managed WordPress Hosting

Risk-Free Trial

Speed tuning built in and kept up every month.

$35per month to start
  • Server page cache and Redis object cache
  • Cloudflare CDN included
  • Core Web Vitals tracking in your dashboard
  • Ongoing monitoring and re-tuning
  • Free site migration
  • Weekly plugin and core updates
  • 24/7 WordPress experts
  • Dedicated CPU power
See Hosting Plans

Starter $35, Growth $55, Pro $120 per month

Faster, or you don’t pay

We test your key pages before we start and again when we finish, with the same Google PageSpeed Insights test. If your site isn’t measurably faster, you don’t pay. If we can’t resolve the issue within the agreed scope of work, no charge is applied to your account.

More WP Farm guides

Step-by-step guides from the team that runs our servers.

Speed questions we hear most

Short answers to the questions site owners ask us before and after a speed project.

Still stuck? Ask a WordPress expert

What is a good PageSpeed score for WordPress?

Lighthouse rates 90 to 100 as good, 50 to 89 as needs improvement and below 50 as poor. The score is a lab test, though. Google Search uses field data from real visitors, so a page that passes LCP, INP and CLS in the field is in good shape even if its lab score is in the 70s.

Why is my site fast for me but slow in PageSpeed Insights?

You are probably seeing a cached page on a fast connection near the server. PageSpeed Insights tests mobile on a slower connection and a mid-range phone, and the first run may miss the cache. Test logged out, on mobile, three times.

How many plugins is too many?

There is no set number. One heavy plugin can slow a site more than twenty light ones. Measure with Query Monitor and remove or replace the plugins that cost the most time.

Do I need a caching plugin if my host already caches pages?

Usually not. Two page caches doing the same job can conflict and serve stale or broken pages. Ask your host what they cache first, then add only what is missing.

Does a CDN help if most of my visitors are local?

Yes. A CDN takes static files off your server, absorbs traffic spikes and adds HTTP/3 and Brotli. With full-page edge caching, it also cuts TTFB for every visitor.

How long until Google sees my improvements?

Field data covers the last 28 days, so expect about four weeks before PageSpeed Insights and Search Console fully reflect a fix. Lab tests show the change right away.

Can speed work break my site?

It can, mostly when JavaScript is deferred or delayed. Work on a staging copy, keep a backup, and test menus, forms and checkout after every change.

Can WP Farm do this for me?

Yes. Our Speed Optimization service is $200 per site, with a before and after PageSpeed report and our Fast or It’s Free Guarantee. Hosting plans include ongoing tuning from $35 a month.

Find out what is slowing your site

Run the free speed test, then fix it with this guide or hand it to us. Fast or it’s free.