Production Tuning
Four switches. Measurable differences. No guesswork.
PerfLocale is fast by default. These four settings are what take it from fast to imperceptible, in order of impact.
What to expect
Measured on the PerfLocale test harness - WordPress 6.9, PHP 8.4, MariaDB 10.6, 5 active languages, 200 translation groups, 1000 posts. Warm-cache second request. Your numbers will vary, but the direction of the delta is what matters.
| Request | Default | With object cache | Reduction |
|---|---|---|---|
| Homepage | 102 queries / 10 ms | 13 queries / 2.7 ms | −88% q / −73% ms |
Localised home (/de/) |
107 queries / 14.5 ms | 13 queries / 3.2 ms | −88% q / −78% ms |
| Blog archive (10 posts) | 112 queries / 13 ms | 14 queries / 4.4 ms | −88% q / −66% ms |
Query counts don’t scale with site size. Same numbers on a 15-post site and a 1000-post site - PerfLocale’s caches are bounded per request, not per total content.
1. Persistent object cache
The single biggest win. Turns ~100 queries per page into ~13. Every WordPress site benefits; multilingual sites benefit most because PerfLocale reads small data structures (language list, translation links, slug translations) on every request - all of those become free.
Redis (recommended)
# Install Redis + the PHP extension
sudo apt install php8.3-redis redis-server
sudo systemctl enable --now redis-server
# Install & activate the WordPress plugin
wp plugin install redis-cache --activate
wp redis enable
Verify:
wp eval 'echo wp_using_ext_object_cache() ? "YES" : "NO";'
# → YES
Alternatives
- Memcached - simpler, functionally equivalent for this workload. Install the Memcached drop-in.
- Object Cache Pro - commercial, tighter WooCommerce integration. Often bundled with managed hosts (Kinsta, Pressable, Cloudways).
2. PHP OPcache
Caches compiled PHP bytecode. Bundled with PHP 5.5+, almost always available, but defaults are too small for WordPress - hot files get evicted. Give it room:
# /etc/php/8.3/fpm/php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.interned_strings_buffer=32
opcache.validate_timestamps=0 ; production only - requires fpm reload on deploy
opcache.save_comments=1 ; required for WordPress
Verify:
wp eval 'print_r( opcache_get_status( false )["opcache_statistics"] );'
# Look for "opcache_hit_rate" — should be > 99% on a warm pool.
validate_timestamps=0 means OPcache won’t notice file edits. systemctl reload php-fpm after every deploy. Don’t set this on a dev machine.
3. CDN edge caching
The fastest request is the one PHP never sees. With a CDN in front, most visitors get HTML from the edge. PerfLocale ships two features that make edge caching work correctly on multilingual sites:
Cache-Tag headers
Enable at Settings → Advanced → CDN Cache-Tag Headers. PerfLocale emits a tag on every response:
Cache-Tag: perflocale,lang:fr_FR,lang-slug:fr,post:42,post-type:post
Your CDN (Cloudflare Enterprise, Bunny, Fastly, KeyCDN…) can then purge surgically - flush everything tagged lang:fr_FR, or just post:42, without nuking the whole cache. Full reference: Cache-Tag Headers.
Edge language detection
On Cloudflare Workers, Vercel Edge, or Netlify Edge, you can resolve the visitor’s language at the edge and include it in the cache key. Result: / caches separately per language, zero round-trips to WordPress for language detection. Full guide: Edge Integration.
One thing to avoid: Vary: Accept-Language. Shreds hit rate - every distinct browser Accept-Language header produces a separate cache entry. PerfLocale deliberately does not emit this header. Use URL-based routing (/de/, subdomain, or per-domain) or edge-hint headers instead.
4. String-translation mode
Under Settings → Performance, choose how UI string translations are stored:
| Mode | Speed | When to use |
|---|---|---|
| Files (default) | 8–12% faster on pages with heavy UI-string usage | Default. Translations compiled into .l10n.php files - zero DB cost per string lookup. |
| Database | Slightly slower, zero filesystem writes | Read-only filesystems (some container / serverless WP deployments), or permission issues with the translations directory. |
For normal production servers, keep the default.
Measuring your own numbers
Drop this into wp-content/mu-plugins/perf-log.php for a tuning session. Remove it when done.
<?php
if ( ! defined( 'SAVEQUERIES' ) ) define( 'SAVEQUERIES', true );
add_action( 'shutdown', function () {
global $wpdb;
$total = 0;
foreach ( $wpdb->queries ?? [] as $q ) $total += (float) ( $q[1] ?? 0 );
file_put_contents(
WP_CONTENT_DIR . '/perf.log',
sprintf( "%-40s queries=%3d time=%6.2fms\n",
$_SERVER['REQUEST_URI'] ?? '?',
count( $wpdb->queries ?? [] ),
$total * 1000 ),
FILE_APPEND
);
}, 9999 );
curl -s -o /dev/null https://example.com/
curl -s -o /dev/null https://example.com/de/
tail /wp-content/perf.log
Run each URL twice - the second run is the warm-cache number that matters. If your numbers are more than 30% worse than the benchmarks above, one of the four layers isn’t doing its job. Check them in order: object cache, OPcache, CDN.
Related
- CDN Cache-Tag Headers - per-language CDN purging
- Edge Integration - Cloudflare Workers / Vercel / Netlify
- Performance feature page - the architectural choices that make PerfLocale fast by default