Fix Slow LCP on Contao (Speed Guide) | OpsBlu Docs

Fix Slow LCP on Contao (Speed Guide)

Reduce Contao LCP by enabling page cache, optimizing image sizes in the file manager, and deferring non-critical Contao layout CSS.

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

  1. Contao Manager -- check cache statistics and hit rates at System > Maintenance
  2. Symfony Profiler (dev mode) -- access /_profiler for render time breakdown per content element and module
  3. PageSpeed Insights -- test homepage, news listing pages, and event calendar pages separately
  4. 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 async or defer
  • Contao's Google Analytics extension outputs async gtag.js by default
  • If using Matomo, configure the tracking code in the page layout footer section