Moodle's caching lives in the Universal Cache (MUC), which routes cache 'definitions' (application, session and request data) to cache 'stores'. The high-impact move is to add a Redis store and map the Application, Session and Locking caches to it, so repeated lookups are served from memory instead of the database. Then speed up delivery: turn OFF theme designer mode, turn ON CSS/JS minification and caching, set long browser-cache headers, use X-Sendfile for large files, and put a CDN in front of static assets and media. Together this cuts database load and page latency sharply. edzlms ships Moodle with Redis MUC and a CDN pre-configured.
Key takeaways
- Moodle's MUC maps cache definitions (application, session, request) to stores - move the heavy ones to Redis.
- Add a Redis cache store and map Application, Session and Locking caches to it to take load off the database.
- Theme designer mode must be OFF in production - it disables theme CSS caching and makes every page slower.
- Enable CSS/JS minification and caching, and set long browser-cache headers so returning visitors re-download nothing.
- Use X-Sendfile so the web server (not PHP) streams large files, and a CDN to serve static assets and media near the learner.
- This is Spoke 3 of the Moodle performance series - it builds on the architecture and tuning spokes.
Spoke 3 of our Moodle Performance Optimization Guide (2026). It follows Spoke 1 (architecture) and Spoke 2 (PHP/DB tuning) - now we make Moodle stop repeating work and deliver assets faster.
How Moodle caching actually works: MUC
Moodle's Universal Cache (MUC) is the framework that decides what gets cached and where. It has two halves: cache definitions (the kinds of data Moodle caches - config, language strings, course structures, sessions) and cache stores (where that data physically lives - filesystem, database, or Redis). By default many definitions fall back to the database or disk, which is exactly what you want to change. Point the busy caches at Redis and repeated lookups never touch MariaDB.
There are three cache modes: Application (shared across all users - config, strings, course data), Session (per-user session data) and Request (lives for a single request, always in memory). Application and Session are the ones worth moving to Redis.
1. Add a Redis store and map the caches
First, point Moodle sessions at Redis in config.php (also covered in Spoke 2), then add a Redis cache store in the UI and map the Application and Locking caches to it.
// config.php - Redis for sessions $CFG->session_handler_class = '\\core\\session\\redis'; $CFG->session_redis_host = '127.0.0.1'; $CFG->session_redis_port = 6379; $CFG->session_redis_prefix = 'mdl_sess_'; $CFG->session_redis_acquire_lock_timeout = 120; $CFG->session_redis_lock_expire = 7200;
Then in Moodle: Site administration → Plugins → Caching → Configuration. Under Add store instance choose Redis, enter the host/port, and save. Scroll to the cache definitions and map Application and Locking to your new Redis store (leave Request as the default in-memory static). Purge all caches afterwards.
Result: config, language strings, course structures and locks are all answered from Redis memory - a big cut in database queries per page.
2. Stop shipping uncached, unminified front-end
Two settings silently slow every page if they're wrong in production:
- Theme designer mode (Appearance → Themes → Theme settings) must be OFF. When on, Moodle recompiles and re-sends theme CSS on every request for live editing - great in dev, a serious tax in production.
- CSS/JS minification & caching - ensure
cachejsand CSS caching are enabled (they are by default when designer mode is off) so Moodle serves combined, minified, cache-busted assets.
Then let browsers cache those static assets aggressively - Moodle versions its asset URLs, so long TTLs are safe:
# Nginx - long cache for Moodle static assets & media
location ~* ^/(theme|lib|pluginfile\.php|tokenpluginfile\.php) {
expires 30d;
add_header Cache-Control "public, immutable";
}
# General static types
location ~* \.(css|js|png|jpg|jpeg|gif|svg|woff2?)$ {
expires 30d; add_header Cache-Control "public";
}3. Serve files & media the fast way (X-Sendfile + CDN)
By default Moodle streams every file download through PHP, which ties up a PHP-FPM worker for the whole transfer - painful for videos and large SCORM packages. X-Sendfile hands the actual file streaming to the web server, freeing PHP immediately:
// config.php - enable X-Sendfile (Nginx uses X-Accel-Redirect)
$CFG->xsendfile = 'X-Accel-Redirect';
$CFG->xsendfilealiases = array('/dataroot/' => $CFG->dataroot);# Nginx - internal location that actually streams the file
location /dataroot/ {
internal;
alias /var/moodledata/;
}Finally, put a CDN (Cloudflare, or a cloud CDN) in front of the site so static assets, theme files and cacheable media are served from an edge location near the learner instead of your origin. For geographically spread audiences this is one of the biggest perceived-speed wins, and it shields your origin from traffic spikes. Media/moodledata offload to object storage pairs naturally with the horizontal architecture from Spoke 1.
- 1Point sessions at Redis
Set the Redis session handler in config.php, then purge caches.
- 2Add a Redis cache store
In Caching > Configuration, add a Redis store and map Application and Locking caches to it.
- 3Turn theme designer mode OFF
In production this is essential - it re-sends theme CSS on every request when left on.
- 4Enable minification + browser caching
Confirm CSS/JS caching is on and set long Cache-Control headers for versioned static assets.
- 5Enable X-Sendfile + a CDN
Let the web server stream large files, and serve static assets/media from a CDN edge.
- 6Purge and verify
Purge all caches, then confirm assets return from cache and file downloads no longer occupy PHP workers.
Default Moodle delivery
- MUC caches on database/filesystem
- Theme designer mode sometimes left on
- Every file download runs through PHP
- Assets re-downloaded each visit
- All requests hit the origin server
Cached + CDN (edzlms)
- Application/Session/Locking caches in Redis
- Designer mode off; minified, cached CSS/JS
- X-Sendfile streams files from the web server
- Long browser cache on versioned assets
- CDN serves static assets & media at the edge
Want Redis + CDN set up for you?
edzlms runs Moodle with Redis-backed MUC, X-Sendfile and a CDN pre-configured and monitored, on performance-tuned hosting with India data residency. Share your setup and we'll benchmark the difference.
Check theme designer mode first
If Moodle feels slow site-wide and CSS reloads constantly, theme designer mode is probably ON in production. Turning it off is a one-click win before you touch anything else.
Frequently asked questions
What is Moodle's MUC (Universal Cache)?
MUC is Moodle's caching framework. It maps cache definitions (application, session and request data like config, language strings and course structures) to cache stores (filesystem, database or Redis). Pointing the busy caches at Redis serves them from memory instead of the database.
Which Moodle caches should I put in Redis?
Map the Application and Locking caches to a Redis store, and point sessions at Redis via config.php. Leave the Request cache as the default in-memory static. This removes repeated config, string, course-data and lock lookups from the database.
Why is theme designer mode a performance problem?
With theme designer mode on, Moodle recompiles and re-sends theme CSS on every request so you can edit it live. That's useful in development but a significant tax in production - it must be turned off so CSS is cached and minified.
What is X-Sendfile and why use it in Moodle?
Normally Moodle streams every file download through PHP, tying up a PHP-FPM worker for the whole transfer. X-Sendfile (X-Accel-Redirect on Nginx) hands the streaming to the web server, freeing PHP instantly - important for videos and large SCORM packages.
Does a CDN help Moodle?
Yes, especially for geographically spread learners. A CDN serves static assets, theme files and cacheable media from an edge location near the user, cutting latency and shielding your origin from traffic spikes. Pair it with long browser-cache headers on versioned assets.
Do I need to purge caches after changing cache settings?
Yes. After adding a Redis store, remapping caches or changing asset settings, purge all caches (Site administration > Development > Purge caches) so Moodle rebuilds with the new configuration.
Make Moodle stop repeating itself
Redis-backed MUC, minified cached assets, X-Sendfile and a CDN together deliver a dramatically faster site - but wiring them correctly takes care. We configure, benchmark and monitor all of it as managed edzlms Moodle.
The rest of the series: the performance pillar, Spoke 1 (architecture) and Spoke 2 (PHP/DB tuning).
Prefer to pick a slot directly? Grab a time here, or email marketing@edzlms.com.
Written by Mihir Jana, founder of edzlms - connect on LinkedIn.