WordPress Performance Optimization (Caching, Database)
Default WordPress configuration is slow: each page request executes 30–100 SQL queries, loads dozens of plugins, and regenerates HTML from scratch. Proper optimization achieves Time to First Byte < 200 ms and Core Web Vitals in the green zone.
Audit: What's Slowing Things Down
Before optimization—measure. Tools:
- Query Monitor (plugin) — shows all SQL queries, hooks, slow queries per page
- New Relic or Tideways — PHP profiler for production
- GTmetrix / PageSpeed Insights — external metrics
Typical causes of slow performance:
- No PHP OPcache
- No object cache (Memcached/Redis)
- Slow plugins (plugin ≠ culprit, but WP_Query inside—often)
-
wp_optionstable with thousands of autoload options - Huge
wp_postmetatable - No page caching
PHP OPcache
# /etc/php/8.3/fpm/conf.d/10-opcache.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=0 ; 0 in production (don't check changes)
opcache.validate_timestamps=0 ; 0 in production
opcache.fast_shutdown=1
opcache.jit=tracing
opcache.jit_buffer_size=64m
OPcache performance gain—30–50% reduction in PHP execution time.
Redis for Object Cache
apt install redis-server
# In WordPress
composer require wpackagist-plugin/redis-cache
wp-config.php:
define('WP_CACHE_KEY_SALT', 'yourdomain.com_');
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);
Redis caches WP_Query results, options (get_option), user data. Repeated page request: with Redis—1–5 SQL queries instead of 30–100.
Page Caching
For high-load sites—Nginx FastCGI Cache (bypassing PHP entirely):
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
server {
location ~ \.php$ {
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
# Don't cache authorized users and shopping cart
set $skip_cache 0;
if ($http_cookie ~* "wordpress_logged_in|woocommerce_cart_hash") {
set $skip_cache 1;
}
if ($request_method = POST) { set $skip_cache 1; }
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
# ...
}
}
Cached pages are served with TTFB < 10 ms.
Database Cleanup
-- Remove old revisions (keep last 5 per post)
DELETE p FROM wp_posts p
LEFT JOIN (
SELECT ID FROM wp_posts
WHERE post_type = 'revision'
ORDER BY post_date DESC
LIMIT 5
) keep ON p.ID = keep.ID
WHERE p.post_type = 'revision' AND keep.ID IS NULL;
-- Remove orphaned postmeta
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;
-- Analyze autoload options (cause of slow get_option)
SELECT option_name, LENGTH(option_value) as size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 20;
-- Disable autoload for unnecessary options
UPDATE wp_options SET autoload = 'no'
WHERE option_name IN ('_transient_some_plugin_cache', 'some_large_option');
Image Optimization
// WebP conversion on upload (via Imagick)
add_filter('wp_handle_upload', function (array $upload): array {
if (!str_starts_with($upload['type'], 'image/')) return $upload;
$image = new Imagick($upload['file']);
$image->setImageFormat('webp');
$image->setOption('webp:lossless', 'false');
$image->setCompressionQuality(82);
$webp_path = preg_replace('/\.(jpe?g|png)$/i', '.webp', $upload['file']);
$image->writeImage($webp_path);
return $upload;
});
Or via ShortPixel / Imagify plugin with CDN integration.
Typical Optimization Results
| Metric | Before | After |
|---|---|---|
| TTFB (no cache) | 800–2000 ms | 150–400 ms |
| TTFB (with FastCGI cache) | — | 5–15 ms |
| SQL queries per page | 60–120 | 5–15 |
| LCP | 3–6 s | 1–2 s |
Timeline
Basic optimization (OPcache, Redis, database cleanup, page cache)—2–3 days. Full audit with profiling and slow query optimization—5–7 days.







