TTFB (Time to First Byte) is the time from when the user’s browser fires a request to when it receives the first byte back. It’s a core user experience metric and the prefix to every Google Core Web Vitals content load metric.
Good TTFB: under 800ms. Poor: over 1800ms. Nineteen years of WordPress optimization work have taught me to drag TTFB from a typical 2 to 3 seconds down to 400ms. These are my notes from those projects.
TTFB components
TTFB is actually the sum of three sub-metrics:
- DNS lookup: domain resolves to IP (usually cached, under 50ms)
- TCP plus TLS handshake: connection is established (150 to 300ms, initial)
- Server processing time: the server produces the response (most of your TTFB)
80% of optimization work targets server processing time.
Server-side measurement
Measure TTFB components on the backend:
// WordPress example
add_action('shutdown', function() {
$ttfb = (microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000;
error_log("TTFB: {$ttfb}ms");
});For deeper profiling: New Relic, Datadog APM. Break down each request phase (DB, cache, external API).
Database query optimization
50 to 70% of TTFB is usually the database.
Common issues:
1. N+1 queries. When rendering a list, an extra query per item.
// BAD
$posts = get_posts(['posts_per_page' => 20]);
foreach ($posts as $post) {
$author = get_user_by('id', $post->post_author); // 1 query per post
}
// 21 queries total
// GOOD
$posts = get_posts(['posts_per_page' => 20]);
$author_ids = array_unique(array_column($posts, 'post_author'));
$authors = get_users(['include' => $author_ids]);
// 2 queries total2. Missing indexes. Queries are slow because of a full table scan.
EXPLAIN SELECT * FROM wp_postmeta WHERE meta_key = 'popular' AND meta_value = 'yes';
-- type: ALL (full scan) = bad
-- type: ref (index used) = good
CREATE INDEX idx_meta_key_value ON wp_postmeta (meta_key, meta_value(20));3. Heavy JOINs. A 5-table JOIN is slow.
Denormalize, or use a materialized view.
4. autoload option bloat. In WordPress, every row in wp_options with autoload=yes loads on every request.
SELECT option_name, LENGTH(option_value) FROM wp_options WHERE autoload = 'yes' ORDER BY LENGTH(option_value) DESC LIMIT 20;1MB+ autoload options add up fast. Flip the ones you don’t need to autoload=no.
PHP-level optimization
Enable OPcache:
PHP OPcache stops the engine from parsing on every request. php.ini:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0 // in productionIn production, validate_timestamps=0 skips file change checks. After a deploy, run opcache_reset() manually.
OPcache preloading (PHP 7.4+):
// preload.php
opcache_compile_file(__DIR__ . '/wp-settings.php');
opcache_compile_file(__DIR__ . '/wp-includes/class-wp.php');
// ...Point php.ini at it with opcache.preload=/path/preload.php. Hot files preload at boot.
JIT compilation (PHP 8+):
opcache.jit_buffer_size=100M
opcache.jit=tracing5 to 15% perf gain on CPU-heavy workloads.
Caching layers
1. Object cache.
Cache PHP objects between requests. Redis or Memcached.
For WordPress: wp-content/object-cache.php drop-in. Redis Object Cache plugin.
Instant 30 to 50% DB load reduction.
2. Page cache.
Cache rendered HTML. Static files or Redis.
LiteSpeed Cache, WP Rocket, W3 Total Cache. Serve cached HTML to anonymous users, bypass for logged-in.
Typical TTFB drop: from 1 to 2 seconds down to 50ms.
3. Database query cache.
Cache repeated identical queries. MySQL query cache is deprecated (5.7+), so go app-level.
$cache_key = 'top_posts_' . date('Y-m-d-H');
$top = wp_cache_get($cache_key);
if ($top === false) {
$top = expensive_query();
wp_cache_set($cache_key, $top, '', 300);
}4. Full-page cache at the CDN.
Cloudflare, Fastly edge caching. Content never reaches your origin. It’s served from the CDN POP.
For static content, TTFB drops below 100ms globally.
Server infrastructure
Hardware matters:
- SSD vs HDD: SSD is 10x faster on I/O. These days SSD is standard.
- RAM: keep the DB’s working set in memory. Page cache, query cache, app memory.
- CPU cores: PHP-FPM workers run in parallel. Typical formula: cores times 2 workers.
LiteSpeed vs Nginx vs Apache:
LiteSpeed is the performance leader, with a license fee.
Nginx is second, free, great community.
Apache is legacy, with .htaccess convenience.
My preference: LiteSpeed if budget allows, Nginx if not.
External service dependencies
Plugin calls to external APIs inflate TTFB:
- Google Fonts API
- Gravatar API
- Analytics endpoints
- Third-party license checks
Fix:
– Cache remote responses aggressively
– Async/background fetch (WP Cron)
– Local fallback
On one project, seven separate external API calls were padding frontend TTFB. 200 to 500ms each. Caching saved a total of 1.5 seconds.
WordPress-specific quick wins
1. Autoload cleanup.
SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload = 'yes';
-- Total autoload size. Over 1MB needs cleanup.2. wp_options bloat detection.
Plugins add transients but don’t clean them up. Millions of rows accumulate.
SELECT COUNT(*) FROM wp_options WHERE option_name LIKE '_transient_%';Over 10K means time to clean up. WP-CLI: wp transient delete --all.
3. Query Monitor plugin.
Active in dev/staging. Shows queries, memory, and TTFB breakdown for every request.
4. Disable wp-cron, use a real cron.
wp-config.php:
define('DISABLE_WP_CRON', true);System cron:
*/5 * * * * wget -q -O /dev/null https://site.com/wp-cron.phpwp-cron triggers on every page load, and random scheduled tasks choke TTFB. A real cron isolates them.
5. Heartbeat API throttle.
In admin, Heartbeat pings every 15 seconds. In production, relax it to minutes. Heartbeat Control plugin.
Monitoring
Continuously track TTFB in production:
- Real User Monitoring (RUM): web-vitals.js library, actual users.
- Synthetic monitoring: Pingdom, UptimeRobot periodic checks.
- Backend APM: New Relic, Datadog, breakdown by endpoint.
Alert: p95 TTFB over 1.5s is an anomaly.
Wrap-up
TTFB comes down to server processing time. DB query optimization, PHP OPcache, caching layers, infrastructure choice. Each contributes cumulatively.
WordPress-specific quick wins: autoload cleanup, wp-cron fix, heartbeat throttle, Query Monitor profiling. A day’s work typically recovers 1 to 2 seconds on most sites.
Target: sub-800ms TTFB in production. Green in Google Core Web Vitals, positive SEO impact.