General Guide: See Global LCP Guide for universal concepts and fixes.
What is LCP?
Largest Contentful Paint measures when the largest content element becomes visible. Google recommends LCP under 2.5 seconds. Contao is a Symfony-based PHP CMS with a built-in image processing pipeline, page caching, and layout system that all affect LCP.
Contao-Specific LCP Causes
- Page cache disabled -- Contao's built-in HTTP cache is off by default; without it every request runs through the full Symfony/PHP render pipeline
- Image processing on first request -- Contao generates responsive image variants (via
contao/image) on first access, causing slow initial loads - Layout CSS aggregation -- Contao's page layouts can include CSS from multiple modules and content elements, all loaded synchronously in
<head> - Insert tag processing -- Contao's
{{insert_tag}}system runs PHP to resolve tags like{{file::*}}and{{picture::*}}on every uncached request - Module overhead -- front-end modules (news lists, event calendars, forms) execute database queries per page load
Fixes
1. Enable Contao's HTTP Cache
Contao 4.9+ includes a Symfony-based HTTP cache. Enable it:
# config/config.yaml
contao:
http_cache:
enabled: true
default_ttl: 3600 # 1 hour default
private_ttl: 0
Or use the environment variable approach:
# .env.local
CONTAO_PAGE_CACHE=true
Verify cache headers are working:
curl -sI https://yoursite.com | grep -i "cache-control\|x-cache\|age"
# Should see: Cache-Control: public, max-age=3600
Set per-page cache settings in the Contao backend at Page Layout > Cache. Set "Cache time" to at least 3600 for static pages.
2. Pre-Generate Image Variants
Contao's image processing generates responsive variants on first request. Warm the cache at deploy time:
# Contao console command to regenerate image cache
php vendor/bin/contao-console contao:resize-images
# Or warm specific paths
php vendor/bin/contao-console contao:resize-images --image=files/content/hero.jpg
Configure image sizes in contao/image-sizes.yaml:
# contao/image-sizes.yaml
contao:
image:
sizes:
hero:
width: 1920
height: 600
resize_mode: crop
formats:
jpg: [webp, jpg]
png: [webp, png]
thumbnail:
width: 400
height: 300
resize_mode: proportional
formats:
jpg: [webp, jpg]
3. Optimize Content Element Templates
Override Contao's default image content element to add performance hints:
<!-- templates/ce_image.html5 -->
<?php $this->extend('block_searchable'); ?>
<?php $this->block('content'); ?>
<?php if ($this->addImage): ?>
<figure class="image_container">
<?php if ($this->singleSRC): ?>
<img
src="<?= $this->src ?>"
width="<?= $this->imgSize[0] ?>"
height="<?= $this->imgSize[1] ?>"
alt="<?= $this->alt ?>"
<?php if ($this->isFirst): ?>
loading="eager"
fetchpriority="high"
<?php else: ?>
loading="lazy"
<?php endif; ?>
>
<?php endif; ?>
</figure>
<?php endif; ?>
<?php $this->endblock(); ?>
4. Preload LCP Image
Add preload hints in your page layout template:
<!-- templates/fe_page.html5 -->
<?php $this->extend('fe_page'); ?>
<?php $this->block('head'); ?>
<?= $this->parent() ?>
<?php if ($this->isHome): ?>
<!-- Preload hero image -->
<link rel="preload" as="image" href="<?= $this->heroImageUrl ?>" type="image/webp">
<?php endif; ?>
<?php $this->endblock(); ?>
5. Defer Non-Critical CSS
Contao aggregates CSS from all included modules. Split critical from non-critical:
<!-- In your page layout, add critical CSS inline -->
<style>
.mod_article:first-child { min-height: 500px; }
.header { display: flex; height: 80px; }
body { font-family: system-ui, sans-serif; margin: 0; }
</style>
<!-- Defer the aggregated Contao CSS -->
<link rel="preload" href="<?= $this->stylesheets ?>" as="style"
Measuring LCP on Contao
- Contao Manager -- check cache statistics and hit rates at System > Maintenance
- Symfony Profiler (dev mode) -- access
/_profilerfor render time breakdown per content element and module - PageSpeed Insights -- test homepage, news listing pages, and event calendar pages separately
- Test cached vs. uncached -- append
?purge=1(with appropriate permissions) to test uncached render times
Analytics Script Impact
Contao's analytics integration is typically via custom templates or the analytics_contao extension:
- Add tracking scripts to the page layout's "Custom script" section with
asyncordefer - Contao's Google Analytics extension outputs async gtag.js by default
- If using Matomo, configure the tracking code in the page layout footer section