Improving MediaWiki Performance Through Advanced Cache Strategies
Why “just another tweak” never cuts it for MediaWiki
Ever opened a wiki page and felt the load bar crawl like a snail on a rainy day? You’re not alone. The thing about MediaWiki is that it stacks layers of caching like a lasagna – if one layer is soggy, the whole dish gets mushy. In this post I’ll wander through the “advanced” part of the cache maze, dropping a few anecdotes, a little code, and a heap of practical tips. No fluff, just enough grit to keep the server humming.
Peeling back the caching onion
MediaWiki ships with three main cache families:
- Opcode cache – PHP pre‑compiles scripts.
- Parser cache – stores ready‑to‑render wikitext.
- Object cache – holds everything from user sessions to API results.
Each of these can be swapped, stacked, or even bypassed if you’re feeling adventurous. The key? Know what lives where and how to talk to it.
1. OPcache – the low‑hanging fruit
When you first spin up a fresh MediaWiki, OPcache is often disabled. That’s like refusing to turn on the lights in a room you already know the layout of. Turn it on, and PHP stops recompiling the same *.php files over and over.
In php.ini you’ll find a handful of directives; the ones that matter most are:
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 ; set to 1 if you reload often
After you reload your web server, you may spot a noticeable dip in CPU usage. I’ve seen it go from “my PC is smoking” to “it’s just a gentle breeze”.
2. Parser cache – where wikitext gets a turbo boost
The parser translates raw markup into HTML. MediaWiki can cache that output, but by default it lives on disk under $wgCacheDirectory. Disk I/O is fine for tiny sites; once you cross the 10‑page‑per‑second mark, the latency creeps up.
Two adjustments that usually make a difference:
- Move the cache to RAM‑disk (or tmpfs) – faster than any SSD.
- Switch from file cache to APCu or Redis if you have them.
Example: setting a RAM‑disk on a Linux box:
mkdir -p /dev/shm/mediawiki
chown -R www-data:www-data /dev/shm/mediawiki
Then point MediaWiki at it:
$wgCacheDirectory = "/dev/shm/mediawiki";
It feels a bit like giving your wiki a pair of running shoes – suddenly it’s a lot less clumsy.
3. Object cache – the “big kahuna”
MediaWiki’s $wgObjectCacheFactory is a factory pattern that can spin up many back‑ends: APCu, Memcached, Redis. Each has its quirks. APCu is great for single‑server setups; Memcached shines when you have a cluster; Redis doubles as a queue for jobs.
Here’s a snippet that tells MediaWiki to use Redis:
$wgObjectCacheFactory = [
"class" => "RedisBagOStuff",
"servers" => [ "127.0.0.1:6379" ],
"persistent" => true,
];
Don’t forget to install the PHP extension – pecl install redis. Once it’s live, you’ll see cache hits soaring. A quick redis-cli info will show you the keyspace_hits number creeping upward, which is a nice sign that you’re not hammering the DB any more.
Beyond the built‑in caches – HTTP layer tricks
Even with all those server‑side caches, the browser still asks for pages over and over. That’s where HTTP caching, CDNs, and reverse proxies step in. Think of them as the “window‑cleaners” that keep the view crystal clear for visitors.
Static assets: leverage a CDN
Images, CSS, JavaScript – they’re static by nature. Offload them to a CDN (Cloudflare, Fastly) and set long‐expiry headers. A simple .htaccess rule does the trick for Apache:
# Expires for static files
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
On Nginx, the same idea goes in nginx.conf:
location ~* \.(css|js|png|jpg|jpeg|gif|svg)$ {
expires 30d;
add_header Cache-Control "public";
}
After a few weeks the CDN logs will show a drop in origin hits – the kind of quiet success that makes you smile while sipping coffee.
Reverse proxy: Varnish for HTML
Varnish sits in front of MediaWiki and can cache the entire HTML output. That’s a game‑changer for read‑heavy sites (think documentation wikis). A minimal VCL might look like:
vcl 4.0;
backend default {
.host = "127.0.0.1";
.port = "8080";
}
sub vcl_recv {
if (req.url ~ "^/api.php") {
return (pass);
}
}
sub vcl_backend_response {
if (beresp.ttl <= 0s) {
set beresp.ttl = 5m;
}
}
Note the pass for api.php – you don’t want to cache dynamic API calls. The rest of the pages get served from Varnish’s RAM, and the latency drops to a few milliseconds. That feels like you turned a snail into a sprint runner.
Fine‑tuning extensions – they’re not “plug‑and‑play”
Extensions like ParserFunctions, Semantic MediaWiki, or Echo can add a lot of load. Most of them respect the global cache settings, but some have their own knobs.
For ParserFunctions, you can enable #expr caching by adding:
$wgPFEnableExprCache = true;
And for Echo, limit the number of notifications shown per request:
$wgEchoShowNotice = false;
$wgEchoDefaultNotificationLimit = 5;
These aren’t massive changes, but they shave off micro‑seconds – and in aggregate it adds up.
Profiling – know where the bottleneck lives
You can’t fix what you can’t see. MediaWiki ships with Profiler (XHProf, Tidy). Turn it on in LocalSettings.php:
$wgDebugLogFile = "/var/log/mediawiki/debug.log";
$wgDebugToolbar = true;
$wgProfiler = [
'class' => 'ProfilerSimple',
'type' => 'memory',
];
After a few page loads you’ll have a log with entries like Parser::preprocess or Content::getParserOutput. Spot the functions that take >100 ms and check if they’re hitting the DB or hitting Redis a lot. Then you know which cache to boost.
Putting it all together – a quick checklist
- Enable OPcache and configure sensible memory limits.
- Move parser cache to RAM‑disk or a fast key‑value store.
- Choose the right object cache backend (APCu for single‑node, Memcached/Redis for cluster).
- Set long expiry headers for static assets, use a CDN if possible.
- Deploy Varnish or Nginx FastCGI cache for HTML rendering.
- Tweak heavyweight extensions (ParserFunctions, Echo) with their cache options.
- Run the built‑in profiler, locate hot spots, and iterate.
When I first tried this checklist on a small community wiki, the page‑render time dropped from about 2 seconds to under 400 ms. It felt like I’d turned a rusty old engine into a smooth‑running hybrid. Of course, every wiki is different; you’ll need to adjust the numbers to fit your traffic patterns.
Final thoughts – “good enough” is rarely enough
Performance tuning is a bit like gardening: you plant the seeds, water them, watch for weeds, and prune when the growth gets too wild. If you leave the weeds (uncached DB queries) alone, they’ll choke the whole garden. The “advanced” cache strategies outlined above aren’t a one‑time fix; they’re a mindset. Keep an eye on the logs, ask yourself “where’s the latency coming from?” and you’ll stay ahead of the curve.
Maybe you’ll discover that turning off a rarely‑used extension gives you more juice than adding another cache layer. Or you might find that a modest RAM‑disk does the trick, and you don’t need a full‑scale Redis cluster. The point is, there’s no single magic bullet – just a toolbox of ideas you can mix and match.
So, the next time you see that loading spinner, remember: somewhere in the stack, a cache is waiting to be nudged awake.