Skip to content

Scaling WordPress: Redis Object Cache, Query Optimization, and Page Caching

A

Al Amin Ahamed

Senior Engineer

8 min read
𝕏 in

WordPress sites are slow by default. Not because WordPress is inherently slow — it can serve tens of thousands of requests per day — but because the defaults are misconfigured and every plugin adds overhead. Here's how to fix it.

Baseline: Know Your Slow Pages

Before optimizing anything, measure:

# TTFB (Time To First Byte) from the shell curl -w "\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \ -o /dev/null -s https://example.com # Query count from inside WordPress define('SAVEQUERIES', true); // in wp-config.php during debug

Target: TTFB < 200ms, < 30 DB queries per page.

Object Caching with Redis

WordPress has a built-in object cache API (wp_cache_get, wp_cache_set) but it's per-request only by default — it doesn't persist between requests.

Install a persistent object cache:

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

Add to wp-config.php:

define('WP_REDIS_HOST', '127.0.0.1'); define('WP_REDIS_PORT', 6379); define('WP_REDIS_DATABASE', 0); define('WP_REDIS_TIMEOUT', 1); define('WP_REDIS_READ_TIMEOUT', 1);

With Redis object caching, expensive queries (post meta, options, term relationships) are cached in-memory across requests. This alone typically cuts response time by 30-60%.

Query Optimization

The most common performance killer: WP_Query without no_found_rows:

// Bad — runs COUNT(*) even if you don't need pagination $query = new WP_Query(['post_type' => 'product', 'posts_per_page' => 6]); // Good — skip the COUNT(*) when you don't paginate $query = new WP_Query([ 'post_type' => 'product', 'posts_per_page' => 6, 'no_found_rows' => true, 'update_post_meta_cache' => false, // skip if you don't use post meta 'update_post_term_cache' => false, // skip if you don't need terms ]);

Page Caching

Object caching speeds up PHP execution. Page caching serves entire pages from disk — zero PHP, zero DB.

For Nginx + FastCGI:

fastcgi_cache_path /tmp/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m; fastcgi_cache_key "$scheme$request_method$host$request_uri"; location ~ \.php$ { fastcgi_cache WORDPRESS; fastcgi_cache_valid 200 60m; fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; add_header X-FastCGI-Cache $upstream_cache_status; }

WP Super Cache and W3 Total Cache achieve the same thing at the plugin level if you don't control the server config.

CDN for Assets

Every image, CSS file, and JavaScript bundle should be served from a CDN. CloudFront or Bunny CDN with WordPress:

// Rewrite asset URLs to CDN add_filter('wp_get_attachment_url', function (string $url): string { return str_replace(site_url(), 'https://cdn.example.com', $url); });

Database Maintenance

# Optimize all tables (removes overhead from deleted rows) wp db optimize # Delete post revisions older than 30 days wp post delete $(wp post list --post_type=revision --before="30 days ago" --format=ids) --force # Remove transients that have expired wp transient delete --expired

Add these to a weekly cron job. WordPress accumulates tens of thousands of expired transients in wp_options over time, which slows every page that reads the options table.

Share 𝕏 in
A

Al Amin Ahamed

Senior software engineer & AI practitioner. Building things in Laravel, PHP, and TypeScript.

About me →

One email a month. No noise.

What I shipped, what I read, occasional deep dive. Unsubscribe anytime.