Diagnosing Contao Cache Failures: Stale Frontend Output in Production
Diagnostic guide for Contao 4.x/5.x cache failures: stale frontend output, missing template changes, or cache:clear not reflecting updates. Covers Symfony, Contao, OPcache, HTTP cache/Varnish, Doctrine metadata, and asset fingerprinting with ordered checks and fixes.
15 Oct 2026, 13:08 UTC

Problem and Takeaway
After editing a template or replacing an asset in Contao 4.x/5.x, the production frontend still serves the old version. Running php bin/console cache:clear reports success but nothing changes. The root cause is almost always that one of several independent cache layers — Symfony’s compiled container, Contao’s internal tl_cache table, OPcache, an HTTP cache (Varnish/CDN), or Doctrine’s metadata cache — has not been invalidated. This guide gives you a repeatable diagnostic path, the exact commands to clear each layer, and the escalation steps when the usual commands don’t work.
Symptom → Cause → Diagnostic Quick Reference
| Observable Symptom | Likely Cache Layer | Quick Diagnostic |
|---|---|---|
| Frontend shows outdated content after template or content edits | Symfony cache (var/cache/prod/), Contao tl_cache, OPcache |
Check file mtimes in var/cache/prod/; query SELECT MAX(tstamp) FROM tl_cache;; run php -v for OPcache status |
cache:clear completes but changes remain invisible |
Multiple layers (HTTP cache/Varnish, Contao page cache, browser cache) or wrong environment | Verify APP_ENV=prod; curl -I for X-Cache headers; inspect varnishlog if present |
| Backend reports “Cache cleared” yet frontend unchanged | Contao page cache (tl_page.cache) or fragment/ESI cache not purged |
SELECT id, alias, cache FROM tl_page WHERE cache=1;; look for <esi:include tags in response body |
| Replaced images/assets not updating | Asset fingerprint unchanged (contao:assets:cache:clear not run) or CDN/browser caching old URL |
Compare fingerprints in public/assets/ with tl_files.uuid; check Cache-Control headers on asset responses |
| Doctrine entity/schema changes not reflected | Doctrine ORM metadata/proxy cache (APCu/Redis) stale | Inspect config/packages/doctrine.yaml for metadata_cache_driver; run php bin/console doctrine:cache:clear-metadata |
Ordered Checks — Run in Sequence
- Confirm environment. Ensure
APP_ENV=prodandAPP_DEBUG=0in.env.localor server configuration. Runphp bin/console about | grep -E 'cache|environment'to verify. - Clear Symfony cache without warmup.
php bin/console cache:clear --no-warmup. This avoids re-populating stale data from warmers that might read outdated sources. - Clear Contao’s internal caches.
php bin/console contao:cache:cleartruncatestl_cache, purges page cache (tl_page.cache), and removes fragment/ESI entries. Run during low traffic to avoid lock contention. - Regenerate asset fingerprints.
php bin/console contao:assets:cache:clearrewrites asset hashes inpublic/assets/and updatestl_filesrecords. - Reset OPcache. OPcache reset via
php -r 'opcache_reset();'only affects the current PHP-FPM worker. A fullsystemctl reload php-fpm(or equivalent) is required for all workers. - Purge HTTP cache / Varnish / CDN. Send
PURGEorBANrequests to your edge cache. For Cloudflare, use the API or dashboard “Purge Everything”. - Verify page cache flags. Run
UPDATE tl_page SET cache=0 WHERE cache=1;if specific pages are stuck, or confirmSELECT COUNT(*) FROM tl_page WHERE cache=1;returns zero. - Clear Doctrine metadata cache if schema changed.
php bin/console doctrine:cache:clear-metadata(andclear-query,clear-resultif configured).
Fixes Mapped to Findings
- Stale Symfony cache →
cache:clear --no-warmupfollowed bycache:warmup(optional, but ensures a clean warmup). - Stale Contao page/fragment cache →
contao:cache:clearor direct SQLUPDATE tl_page SET cache=0. - Stale asset fingerprints →
contao:assets:cache:clearregenerates hashes; then purge CDN. - OPcache holding old bytecode → Restart PHP-FPM (preferred) or call
opcache_reset()on every worker. - HTTP cache/Varnish/CDN serving old responses → Issue
PURGE/BANfor affected URLs; verify withcurl -IforX-Cache: MISS. - Doctrine metadata cache stale →
doctrine:cache:clear-metadata(and related cache clear commands).
Escalation Criteria
If all above layers are cleared and the frontend still shows stale output, proceed with these deeper checks:
- Deployment integrity. Confirm the deployment wrote files to the correct document root (watch for symlink switches between releases).
- OPcache corruption. Restart PHP-FPM entirely; a simple reload may not flush corrupted shared memory segments.
- Custom cache warmers. Inspect
kernel.bootedlisteners for warmers that skip Contao’s cache listeners (common in heavily customized projects). - Enable kernel.debug temporarily. Set
APP_DEBUG=1for a single request and use the Symfony profiler to trace cache hits/misses. - Engage hosting/CDN support. Request confirmation that edge cache purge completed; some providers require explicit API calls per zone.
Verification Checklist
php bin/console about | grep -E 'cache|environment'— confirms cache directories and environment.php bin/console cache:pool:list— enumerates all cache pools and their adapters.SELECT COUNT(*) FROM tl_cache;before and aftercontao:cache:clear— should drop to zero.curl -I https://example.com/ | grep -i cache— inspectX-Cache,Age,Cache-Controlheaders.- Check
var/cache/prod/timestamp aftercache:clear— directory should be recreated with current time. - For asset changes: compare
public/assets/images//file.jpgbefore and aftercontao:assets:cache:clear.
Limitations and Version Notes
- Contao 5.x (Symfony 6.x) uses cache contracts; the
--no-optional-warmersflag was removed. Use--no-warmuponly. - Doctrine metadata cache driver (APCu/Redis) is separate from Symfony’s cache pools; clearing one does not clear the other.
- CDN/Cloudflare respects
Cache-Control: max-age; asset fingerprinting viacontao:assets:cache:clearis the primary invalidation method. - Running
contao:cache:clearon high-traffic sites can cause lock contention ontl_cache; schedule during maintenance windows.
Concrete Example: Full Cache Reset Script
#!/usr/bin/env bash
# Run as the web server user (e.g., www-data) in the project root
set -e
php bin/console cache:clear --no-warmup
php bin/console contao:cache:clear
php bin/console contao:assets:cache:clear
php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:cache:clear-query
php bin/console doctrine:cache:clear-result
# OPcache reset via PHP-FPM reload (requires sudo or appropriate permissions)
sudo systemctl reload php8.3-fpm
# If using Varnish
varnishadm "ban req.url ~ ."
# Verify
php bin/console about | grep -E 'cache|environment'
echo "tl_cache rows: $(php bin/console doctrine:query:sql 'SELECT COUNT(*) FROM tl_cache;')"
Adjust the PHP-FPM service name and Varnish command to your stack. The script assumes you have CLI access and sufficient permissions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.