How to Fix a Slow WordPress Site (Real 2026 Case Study + 6-Step Fix Sequence)
WordPress site loading slowly? This 2026 guide walks through the exact six-step fix I use on real client sites — server, plugins, images, caching, JavaScript, database — with measurable before-and-after numbers from a real audit (Lighthouse 38 baseline, LCP 3.5-5.5s), not generic advice.
Why WordPress sites get slow
After 13 years and more than 100 WordPress builds, the slow-site pattern is almost never WordPress itself. It is what sits on top of WordPress: a heavy theme, unoptimised images, a long plugin list, and cheap shared hosting. Together those four layers turn a 500ms server into a 4-second page.
The four specific culprits I see on every problem site:
- Shared hosting with no PHP opcode caching. On a shared server, each PHP request can take 300ms to 800ms just for opcode compilation. That is before a single database query runs. A site on shared hosting with 20 plugins easily hits 3 to 5 seconds uncached.
- Uncompressed images at full resolution. A homepage with six to eight JPEG hero images often ships 3MB to 6MB of image data. On a median mobile connection that is 4 to 8 seconds of transfer alone.
- Plugins loading assets everywhere. A social-share plugin enqueuing 200KB of JavaScript on a blog post where it is not even visible is a real pattern I find weekly. Multiply by 5 to 10 poorly built plugins and you are adding 1MB to 2MB of render-blocking assets on every page.
- No object cache, so WordPress hammers MySQL on every request. WordPress core generates 30 to 80 database queries per page. Without Redis or Memcached, every one of those hits MySQL. On a busy site that is thousands of queries per minute against a single-threaded query cache.
Google considers a 2.5-second Largest Contentful Paint the cut-off between good and poor user experience. Above that, rankings drop and conversion rates fall measurably. Every layer has a known, repeatable fix.
Measure before you touch anything
Before touching a single config file, get baseline numbers. Without measurements the work is guesswork and the gains are invisible.
The four tools that cover every angle:
- Google PageSpeed Insights — reports the Core Web Vitals that Google actually ranks on. Numbers to watch: LCP above 2.5s is poor; CLS above 0.1 is poor; INP above 200ms is poor. Run it on mobile, not just desktop.
- GTmetrix — shows a waterfall of every asset with timings. TTFB above 600ms means the problem is server-side, not frontend. TTFB above 200ms but below 600ms means caching or database. TTFB under 200ms means focus on assets.
- Query Monitor (free WordPress plugin) — lists slow database queries, duplicate queries, and plugin-induced overhead inside the admin. Sort by query count to find offenders immediately.
- Chrome DevTools Network panel — reveals render-blocking resources, resource priorities, and unused chunks that third-party tools miss.
Document starting numbers for every URL type that matters: homepage, archive, product page, cart. Re-measure after each change. If a change does not move the numbers, it did not work. This sounds obvious but most clients I take on have never measured systematically and are surprised when the actual bottleneck is not what they assumed.
Case study: what a truly slow WordPress site looks like
Numbers help. The WaterEgo kayaking community site — an Irish outdoor-activity portal I audited in 2026 — is a textbook example of every layer stacking against performance at once. The measurements below are the actual baseline, not projections.
- Lighthouse mobile score: 38 / 100. Deep in the red band.
- Largest Contentful Paint: 3.5 – 5.5 seconds. Google's cut-off is 2.5 seconds.
- 408 KB of unminified CSS render-blocking on every page.
- 192 KB main.js — 4,388 lines — parsed on every page whether the page used the code or not.
- GSAP and Leaflet loaded on every route, including routes with no map and no animation.
- Hero rendered as a CSS
background-image, which is invisible to the preload scanner. That single decision cost 400–800 ms of LCP. - A
requestAnimationFramecursor loop running continuously on mobile, burning 5–10 ms per frame on devices that had no cursor to begin with.
None of that is unusual. It is the accumulated cost of premium themes, portfolio-inherited habits, and demos that were never meant to ship together. The six-step fix sequence below — server, plugins, images, caching, JavaScript, database — attacks each layer in turn, and each step moves a measurable number.
Step 1 — Fix the server first (TTFB under 200ms)
If Time to First Byte sits above 600ms, no amount of frontend tuning will rescue the page. Server is layer one, and it is the layer most people skip because it feels harder than installing a plugin.
The stack I deploy on every serious site is the same pattern running on the Fine Luxury Property platform: a managed VPS (DigitalOcean, Hetzner, or similar), Nginx in front of PHP 8.2 FPM, Redis as the WordPress object cache, and a tuned MySQL. That combination reliably drops TTFB into the 80 to 200ms range. Shared hosting will never hit those numbers because the underlying machine is sharing CPU and I/O with hundreds of other tenants.
PHP-FPM pool configuration
The default PHP-FPM pool settings are conservative and throttle throughput. On a VPS with 4GB of RAM, these settings handle traffic spikes without hitting process limits:
[wordpress]
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
pm.process_idle_timeout = 10s
request_terminate_timeout = 60s
The pm.max_requests = 500 setting recycles workers after 500 requests, which prevents PHP memory leaks from accumulating. On a shared server you do not get to set this. On your own VPS it takes five minutes and immediately improves stability under load.
PHP OPcache settings
OPcache compiles PHP once and stores bytecode in shared memory. Without it, PHP recompiles every file on every request. These settings belong in your php.ini:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.save_comments=1
opcache.fast_shutdown=1
Set opcache.memory_consumption to at least 128MB on any real WordPress install. A large site with many plugins can need 256MB. After setting these, run php -i | grep opcache to confirm they loaded correctly.
If a VPS is not in scope right now, the next best step is a managed WordPress host: Kinsta, WP Engine, or Cloudways. They run this same kind of stack and handle the operations. What you cannot do is stay on shared hosting and expect professional-grade performance.
Step 2 — Audit and prune plugins
A typical WordPress site I am called in to fix runs 25 to 35 plugins. Most professional sites ship cleanly with 8 to 12. The extra plugins are not neutral — each one adds hooks, database queries, and often assets on every page regardless of whether that page needs them.
Open Query Monitor, sort by query count descending, and identify the worst offenders. The usual suspects:
- Social-share plugins enqueuing scripts on every page
- Slider plugins shipping multiple megabytes of JavaScript and CSS
- Page builders loading their full asset bundle site-wide, even on pages that do not use them
- Analytics plugins firing trackers from PHP instead of using Google Tag Manager
- Form plugins loading assets on every page when the form lives on one page only
- SEO plugins configured to run full analysis on every page load
Heavy plugins vs lightweight alternatives
| Heavy plugin | Typical weight | Lightweight alternative |
|---|---|---|
| Jetpack (full suite) | 500KB+ JS/CSS per page | Individual modules or standalone plugins per feature |
| Contact Form 7 + addons | 80–200KB unconditioned | WPForms Lite, or WP Simple Contact Form (load on form page only) |
| Slider Revolution | 400KB–1MB JS | CSS-only hero with a single transition |
| WooCommerce Social Login | 200KB+ | Native OAuth via theme or headless approach |
| All-in-One SEO | Runs 15–20 queries/page | Yoast SEO or Rank Math on slim configuration |
| Smush (with bulk processing) | 30–50 DB queries per media upload | CLI WebP conversion via ImageMagick (one-time, no overhead) |
Replace what you can, and conditionally load what remains. In WordPress you can wrap wp_enqueue_scripts calls in a conditional check so the plugin's CSS and JS only load on the pages where they are needed. That alone often cuts 200KB to 500KB from pages that previously loaded every plugin's assets blindly.
Step 3 — Compress and serve modern image formats
On a typical WordPress homepage, images make up 60 to 80 percent of the total transferred bytes. Image optimisation is the single biggest quick win on most sites, and it requires zero server configuration to start.
The image checklist I apply to every site:
- Serve WebP or AVIF instead of JPEG and PNG. On this portfolio site, converting six project PNGs to WebP cut their combined weight by roughly 92 percent.
- Generate responsive image sets so phones do not download desktop-resolution files. Use WordPress's built-in
srcsetand register appropriate image sizes for your theme. - Lazy-load everything below the fold with
loading="lazy". WordPress adds this automatically since version 5.5, but check that your theme does not override it. - Declare explicit
widthandheighton every<img>element. Without these attributes the page reflows as images load, destroying Cumulative Layout Shift scores. This is one of the most common CLS culprits I find on audits. - Preload the LCP image. Add a
<link rel="preload">for the above-the-fold hero image so the browser prioritises it over other assets.
Bulk WebP conversion with ImageMagick
For existing sites with hundreds of uploaded images, I run a one-time ImageMagick batch conversion rather than relying on a plugin doing it on the fly. This approach has zero runtime overhead once complete:
# Convert all JPEGs in uploads to WebP (80% quality, good for photos)
find /var/www/html/wp-content/uploads -name "*.jpg" -exec convert {} -quality 80 {}.webp \;
# Convert all PNGs (lossless mode for logos and screenshots)
find /var/www/html/wp-content/uploads -name "*.png" -exec convert {} -define webp:lossless=true {}.webp \;
# Verify average size reduction
du -sh /var/www/html/wp-content/uploads/*.jpg | tail -5
du -sh /var/www/html/wp-content/uploads/*.webp | tail -5
After conversion, configure Nginx to serve the WebP version when the browser supports it via the Accept: image/webp header check. No PHP involved, no plugin overhead, and browsers that do not support WebP automatically get the original.
Step 4 — Three-layer caching
Caching means serving prebuilt pages instead of executing PHP and MySQL queries on every visit. A page that takes 800ms to generate uncached will serve in under 50ms from a warm cache. The math is decisive and the configuration is not complicated once you understand the three distinct layers.
Layer 1: Page cache (the biggest gain)
Full-page caching stores complete HTML responses on disk or in Nginx's FastCGI cache. Subsequent requests for the same URL get the stored HTML without PHP running at all. On a VPS with Nginx, I use FastCGI cache directly in the Nginx config. For shared or managed hosting, WP Rocket's page cache is the most reliable plugin implementation I have tested, followed by LiteSpeed Cache on LiteSpeed servers.
One cache rule matters above all: never cache pages for logged-in users or pages with query strings like cart and checkout. Those must bypass the cache entirely or visitors will see each other's data.
Layer 2: Object cache with Redis
WordPress stores the results of database queries in an object cache. By default that cache lives in PHP memory and is discarded at the end of every request. That means the same query runs again on the next request. Redis keeps the object cache in persistent RAM so it survives across requests.
Setting up Redis as WordPress's object cache requires three steps. First, install the Redis server on your VPS:
sudo apt install redis-server -y
sudo systemctl enable redis-server
sudo systemctl start redis-server
redis-cli ping # should return PONG
Second, install the Predis or phpredis PHP extension, then install the Redis Object Cache plugin (Tillman Recorder's plugin from wordpress.org). Third, add the connection constants to wp-config.php before the /* That's all */ line:
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_CACHE', true );
After enabling the drop-in from the plugin settings page, the object cache is persistent. On a WooCommerce store that was running 120 database queries per page, Redis dropped that to 18 per page on repeat visits. The transient lookups, term cache, and user meta queries all resolved from RAM instead of MySQL.
Redis also eliminates a specific WordPress performance drain: expired transients stored in wp_options. Without Redis, WordPress stores and queries transients in the database. With Redis, transients live in memory and expire automatically. The wp_options table stops growing indefinitely.
Layer 3: Browser cache
Proper Cache-Control headers prevent returning visitors from re-downloading static assets. In Nginx add these rules for the relevant file types:
location ~* \.(css|js|woff2|woff|ttf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.(jpg|jpeg|png|webp|gif|svg|ico)$ {
expires 6M;
add_header Cache-Control "public";
}
With a CDN in front (covered in the next step), the CDN respects these headers and caches assets at the edge, further reducing origin requests.
Step 5 — CDN setup
A Content Delivery Network serves your assets from servers geographically close to the visitor. Without a CDN, every request for an image or a CSS file travels to your origin server wherever it physically sits. With a CDN that same asset loads from an edge node 20 to 50ms away from the visitor regardless of where your server is.
Cloudflare free tier setup
Cloudflare's free plan is the right starting point for most WordPress sites. It handles DNS, DDoS mitigation, basic WAF rules, and caches static assets at the edge globally. Setup takes about 20 minutes:
- Create a Cloudflare account and add your domain. Cloudflare scans existing DNS records automatically.
- Change your domain's nameservers to the two Cloudflare assigns. Allow up to 24 hours for propagation.
- In SSL/TLS settings, set the encryption mode to Full (strict). This requires a valid SSL certificate on your origin server. Never use Flexible — it sends plaintext from Cloudflare to your server.
- Under Caching > Configuration, set the Browser Cache TTL to 1 year for static assets.
- Create a Page Rule for
yourdomain.com/wp-admin/*with Cache Level set to Bypass. Admin pages must never be cached. - Enable Argo Smart Routing if budget allows — it routes requests over Cloudflare's private backbone and typically cuts TTFB by a further 20 to 30 percent.
One important Cloudflare gotcha: by default Cloudflare does not cache HTML. To cache full pages at the edge you need to add a Cache Everything page rule for your public-facing URLs, combined with a bypass rule for logged-in users (check for the wordpress_logged_in_ cookie). Without that rule, only static assets are served from the edge.
BunnyCDN for media offloading
For media-heavy sites, BunnyCDN is an excellent complement to Cloudflare. BunnyCDN is a pure CDN (no DNS proxy, no WAF) with pricing around 0.01 USD per GB for European and North American traffic. It excels at serving images, video, and large file downloads from its global pull zones.
The workflow: configure BunnyCDN as a pull zone pointing at your origin's uploads directory. Then configure WordPress to rewrite media URLs from /wp-content/uploads/ to your BunnyCDN pull zone URL. The CDN Manager plugin handles this automatically. Images load from BunnyCDN's edge; your origin only serves the first uncached request per region.
Step 6 — Defer render-blocking resources
JavaScript that loads in the <head> blocks the HTML parser. The browser stops building the DOM until the script downloads and executes. Even a 50KB script from a legitimate source can cost 300ms to 500ms on a mobile connection if it is parser-blocking.
The fixes in order of impact:
- Move scripts to the footer wherever possible. In WordPress, enqueue scripts with
in_footer: trueinwp_enqueue_scripts. - Add
deferto scripts that do not depend on DOMContentLoaded firing first. - Add
asyncto completely independent third-party scripts like analytics and chat widgets. - Inline critical CSS for above-the-fold content. Defer the full stylesheet load.
- Remove unused CSS. Most premium themes ship 70 to 80 percent of their stylesheet untouched on any given page.
Adding defer to WordPress-enqueued scripts
WordPress does not support adding attributes like defer to enqueued scripts natively before WordPress 6.3. For earlier versions, filter the script tag after output:
add_filter( 'script_loader_tag', function( $tag, $handle, $src ) {
// List of script handles to defer
$defer_handles = array(
'my-plugin-script',
'woocommerce',
'jquery-migrate',
);
if ( in_array( $handle, $defer_handles ) ) {
return str_replace( ' src', ' defer="defer" src', $tag );
}
return $tag;
}, 10, 3 );
Be careful with jQuery. Deferring jQuery while other scripts depend on it executing synchronously will break those scripts. Always test after applying defer to any script that other scripts depend on. On WordPress 6.3 and later, use the wp_script_add_data function with the strategy key set to defer or async directly — no filter hack required.
Step 7 — Database cleanup
A WordPress database that has been running for years without maintenance accumulates significant junk. Post revisions, expired transients, orphaned post meta, spam comments, abandoned auto-drafts. Each row adds to the size of wp_posts and wp_postmeta, slowing every query that scans them.
Before running any cleanup queries, take a database backup. These DELETE statements are irreversible.
Limit post revisions going forward
Add this to wp-config.php before doing anything else. Without it, revisions will accumulate again after cleanup:
define( 'WP_POST_REVISIONS', 5 );
Delete post revisions beyond the limit
-- Delete all post revisions
DELETE FROM wp_posts WHERE post_type = 'revision';
-- Clean up orphaned postmeta from deleted revisions
DELETE FROM wp_postmeta
WHERE post_id NOT IN (SELECT ID FROM wp_posts);
Delete expired transients
Expired transients in wp_options are a major source of table bloat on sites that have run plugins using transients for caching (which is most sites). This deletes both the transient and its expiry record:
DELETE FROM wp_options
WHERE option_name LIKE '_transient_%'
AND option_name NOT LIKE '_transient_timeout_%'
AND option_value IS NOT NULL
AND (
SELECT option_value
FROM wp_options AS t
WHERE t.option_name = CONCAT('_transient_timeout_', SUBSTRING(option_name, 12))
) < UNIX_TIMESTAMP();
-- Also delete the corresponding timeout records
DELETE FROM wp_options WHERE option_name LIKE '_transient_timeout_%'
AND option_value < UNIX_TIMESTAMP();
Optimise tables after cleanup
OPTIMIZE TABLE wp_posts;
OPTIMIZE TABLE wp_postmeta;
OPTIMIZE TABLE wp_options;
OPTIMIZE TABLE wp_comments;
Add missing database indexes
On large WooCommerce stores, two missing indexes alone account for multi-second query times. These add the indexes if they do not exist:
-- Index on wp_postmeta(meta_key) — speeds up all meta_key lookups
ALTER TABLE wp_postmeta ADD INDEX meta_key_idx (meta_key(191));
-- Index on wp_options(autoload) — speeds up option loading on every page
ALTER TABLE wp_options ADD INDEX autoload_idx (autoload);
On a WooCommerce store with 50,000 orders, adding the wp_postmeta(meta_key) index turned a 2.4-second order query into a 22ms query. That single change dropped the page's total database time from 3.8 seconds to 0.4 seconds. The same fix applies to sites with large post archives, member directories, or event listings.
Step 8 — WordPress Multisite speed
If you are running WordPress Multisite and wondering why it is slower than a single-site install, the answer is architectural. Multisite was designed for networks, not performance, and it introduces three bottlenecks that a single-site install does not have.
Per-site table bloat
Every site in a Multisite network gets its own set of tables: wp_2_posts, wp_2_postmeta, wp_2_options, and so on. A network of 50 sites has 50 separate wp_options tables, each potentially containing thousands of autoloaded rows. The database cleanup queries above must be run for each site's table set, not just the main tables.
The practical solution is to script the cleanup across all sites using WP-CLI's --url flag to target each site in the network:
# List all sites in the network
wp site list --fields=blog_id,url
# Run cleanup on each site by blog_id
wp --url=subsite.example.com transient delete --expired
wp --url=subsite.example.com cache flush
sunrise.php hook overhead
Multisite domain mapping loads a sunrise.php drop-in early in the WordPress load cycle. If this file contains database queries to resolve domain mappings — which many domain mapping plugins do — it adds a database round-trip before WordPress has even loaded. On a network with mapped domains on every site, this can add 50ms to 150ms to every request.
The fix is to cache domain mappings in Redis or a static PHP array. The MU Domain Mapping plugin (or the more recent Mercator library) supports Redis-backed domain resolution. Configure it and the overhead drops to a single Redis lookup at approximately 1ms rather than a MySQL query at 20ms to 80ms.
Network-wide object cache configuration
On Multisite, the Redis object cache must be configured to use separate key namespaces per site. Without namespacing, sites can read each other's cached objects, which causes subtle display bugs and incorrect data. Add the blog ID prefix to your wp-config.php Redis configuration:
define( 'WP_REDIS_PREFIX', 'site_' . get_current_blog_id() . '_' );
define( 'WP_REDIS_SELECTIVE_FLUSH', true );
The WP_REDIS_SELECTIVE_FLUSH constant ensures that flushing the cache on one site in the network does not flush the caches of all other sites — a critical setting on production networks where cache warming takes minutes.
Shared plugin and theme loading
Network-activated plugins load on every site in the network. If a plugin is only needed on three of 50 sites, network-activating it adds overhead to the other 47. Audit network-activated plugins regularly and activate per-site those that are not universally needed. Each plugin removed from network activation saves its hook registration time multiplied by every page load on every site.
What results does this process actually produce?
In 13 years across 100+ builds, the pattern is consistent. Here are the headline numbers from real sites:
- RealHomes Modern, the bestselling ThemeForest real estate theme I helped build at Inspiry Themes, was architected on this performance stack from launch. It consistently holds above-90 PageSpeed scores while serving thousands of agencies globally, many running property databases of 10,000 to 50,000 listings.
- Fine Luxury Property, the platform I currently maintain, saw Core Web Vitals improve by 50 percent or more with zero design changes after applying the server, image, and caching work described above. The VPS stack replaced shared hosting and the TTFB dropped from 1.2 seconds to under 150ms on every page.
- AzanGuru, a learning platform I built that now serves 100K+ Android installs, runs the WordPress plus Redis stack for its backend API. Database query time per API call dropped from 180ms average to 22ms after adding the Redis object cache and the postmeta index described in Step 7.
- This portfolio — the WebP conversion alone dropped the homepage payload by more than 300KB, and the explicit width/height sweep on every project card image eliminated CLS from 0.14 to 0.01 across the entire site.
The pattern holds on every engagement: fix the server, clean up plugins, optimise images, add caching at all three layers, set up a CDN, defer JavaScript, and clean the database. Skip any layer and the gains stall. Apply all layers and a 4-second site becomes a sub-second site.
Common mistakes to avoid
After reviewing dozens of failed WordPress performance projects, the same errors appear repeatedly. These mistakes waste hours of work and sometimes make performance worse.
- Measuring on localhost or staging. Local development environments have no network latency, warm caches, and different hardware from production. A site that scores 98 on PageSpeed on localhost will score 55 on the actual production server over mobile. Always measure on production, from a real location, using Lighthouse in throttled mode.
- Adding a caching plugin without enabling the object cache. A page cache alone still lets WordPress hammer MySQL for every user-specific query — WooCommerce cart, member session, search results. Without Redis or Memcached as the object cache, the page cache only solves half the problem.
- Pointing a CDN at uncompressed assets. A CDN serves files faster but does not compress them. If your origin is serving 2MB of uncompressed CSS and images, the CDN will serve that same 2MB faster — not smaller. Enable Brotli or gzip at the origin (and Cloudflare's compression settings) before measuring CDN impact.
- Running database cleanup without limiting revisions first. Deleting 50,000 revisions means nothing if the site generates 200 new ones the following week. Always set
WP_POST_REVISIONSfirst, then clean. Otherwise the cleanup is a one-time fix on an ongoing problem. - Deferring all JavaScript blindly. Deferring scripts that initialise UI components — sliders, tabs, accordions — will break those components visibly on first load because the DOM will be painted before the scripts run. Test every deferred script on real pages. Analytics and chat widgets are safe to defer. jQuery-dependent UI scripts are not unless you have confirmed their dependencies are also deferred in the correct order.
Frequently asked questions
Why is my WordPress site so slow after adding plugins?
Most WordPress plugins register hooks, run database queries, and enqueue scripts on every page load regardless of whether that page actually uses the plugin's functionality. A single poorly-built plugin can add 10 to 30 database queries and 200KB of render-blocking assets on every request. When you install 10 to 20 plugins, that overhead compounds. Install Query Monitor and look at the database query count and total query time to identify which plugins are the worst offenders. The fix is either replacing heavy plugins with lighter alternatives, conditionally loading them only on relevant pages, or removing them entirely if they are not providing value proportional to their cost.
What is a good TTFB for WordPress?
A TTFB under 200ms is the target for a well-optimised WordPress site. Google's own guidance from the Core Web Vitals documentation rates TTFB under 800ms as acceptable for SEO purposes, but in practice a TTFB above 400ms usually indicates a server or caching problem. With a properly configured VPS running Nginx, PHP-FPM, Redis, and FastCGI page cache, I consistently see TTFB of 50ms to 150ms on real production sites. On Cloudflare-fronted sites with a warm cache, edge TTFB is typically 20ms to 50ms regardless of origin server location.
Does WordPress caching really make a difference?
Yes, and the difference is not marginal — it is transformative. An uncached WordPress page running 30 to 80 database queries, executing all active plugin hooks, and rendering PHP templates typically takes 500ms to 2,000ms to generate, depending on server quality and plugin count. That same page served from a FastCGI cache typically returns in 30ms to 80ms. That is a 10x to 60x improvement in response time from a single layer of caching. Adding Redis object cache reduces the database load for uncached requests, typically cutting PHP execution time by 40 to 70 percent. The combination of page cache, object cache, and browser cache is not optional on any site that needs to perform.
How do I speed up WordPress multisite?
WordPress Multisite has three performance-specific problems: per-site table bloat in the database, domain mapping overhead in sunrise.php, and network-activated plugins running on every site regardless of need. The practical fix is to run database cleanup on each site's table set using WP-CLI's --url flag, replace any sunrise.php domain mapping that queries MySQL with a Redis-backed resolver, and audit network-activated plugins so only universally-needed plugins run at the network level. Configure the Redis object cache with per-site key prefixes using WP_REDIS_PREFIX and WP_REDIS_SELECTIVE_FLUSH to isolate site caches and prevent cross-site cache pollution. These four steps together typically reduce per-request time by 30 to 60 percent on networks with more than five sites.
Should I use Cloudflare for WordPress?
Yes, Cloudflare's free tier is the right starting point for almost every WordPress site. It provides global CDN for static assets, DDoS mitigation, a basic Web Application Firewall, and free SSL. The configuration does require attention: set SSL mode to Full (strict), bypass cache for all WordPress admin and WooCommerce cart/checkout pages, and exclude cookies like wordpress_logged_in_ from cache eligibility to prevent logged-in user pages from being cached and served to anonymous visitors. The most common Cloudflare mistake is leaving the SSL mode at Flexible, which sends traffic from Cloudflare's edge to your origin over plaintext HTTP. Use Full (strict) from day one.
How long does it take to speed up a WordPress site?
A typical WordPress performance engagement takes 2 to 5 days of focused work for a site that needs all layers addressed: server migration or configuration, plugin audit, image conversion, caching setup, CDN configuration, JS deferral, and database cleanup. Server migration is the longest step if it involves moving from shared hosting to a VPS. Image conversion and database cleanup can be completed in a few hours on most sites. The caching setup — page cache, Redis object cache, and CDN — takes about half a day to configure correctly and test. The common exception is WooCommerce sites, which need additional testing to ensure caching does not interfere with cart and checkout flows; those typically take a full extra day of validation.
Also Read
- How I run a WordPress VPS stack with 99% uptime on DigitalOcean and Hetzner
- WordPress performance optimisation services — Core Web Vitals and speed audits
- WooCommerce Core Web Vitals optimisation: a practical guide
Need a WordPress performance audit?
If your WordPress site is slow and you want the full stack reviewed properly, get in touch. I have shipped this exact playbook on 100+ projects, including the Fine Luxury Property platform and the AzanGuru learning platform that now serves 100K+ Android installs. I will tell you exactly which layer is the bottleneck and what it will take to fix it.