Fix TYPO3 LCP Issues | OpsBlu Docs

Fix TYPO3 LCP Issues

Speed up TYPO3 LCP by tuning TypoScript page cache, processing FAL images to WebP, and reducing PHP extension overhead.

Learn how to optimize Largest Contentful Paint (LCP) in TYPO3 sites through advanced caching strategies, image optimization, TypoScript configuration, and PHP performance tuning.

Understanding LCP in TYPO3

Largest Contentful Paint measures how long it takes for the largest content element to render. In TYPO3, this is often affected by:

  • PHP execution time (server-side rendering)
  • TYPO3 caching configuration
  • Image loading (FAL processing)
  • TypoScript complexity
  • Extension overhead

LCP Targets

  • Good: < 2.5 seconds
  • Needs Improvement: 2.5 - 4.0 seconds
  • Poor: > 4.0 seconds

Identify LCP Elements

Using Browser DevTools

  1. Open Chrome DevTools (F12)
  2. Go to Performance tab
  3. Click Record and reload page
  4. Stop recording
  5. Look for LCP marker in timeline

Common TYPO3 LCP Elements

  • Hero images in Fluid templates
  • News article featured images
  • Product images (e-commerce)
  • Content element images
  • Background images in layouts

1. TYPO3 Caching Optimization

Enable All Cache Types

Admin Tools → Settings → Configuration Presets

Select Production preset which enables:

  • Page cache
  • TypoScript cache
  • Database query cache
  • PHP opcode cache

Manual Cache Configuration

typo3conf/LocalConfiguration.php or via Admin Tools → Settings:

'SYS' => [
    'caching' => [
        'cacheConfigurations' => [
            'pages' => [
                'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class,
                'options' => [
                    'compression' => true,
                ],
            ],
            'pagesection' => [
                'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class,
            ],
        ],
    ],
],

Install Redis extension:

composer require typo3/cms-redis

Configure in LocalConfiguration.php:

'SYS' => [
    'caching' => [
        'cacheConfigurations' => [
            'pages' => [
                'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
                'options' => [
                    'hostname' => '127.0.0.1',
                    'port' => 6379,
                    'database' => 0,
                    'compression' => true,
                ],
            ],
            'pagesection' => [
                'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
                'options' => [
                    'hostname' => '127.0.0.1',
                    'port' => 6379,
                    'database' => 1,
                ],
            ],
        ],
    ],
],

TypoScript Cache Configuration

# Set cache lifetime
config {
    cache_period = 86400
    sendCacheHeaders = 1
    cache_clearAtMidnight = 1
}

# Enable static file cache
[applicationContext == Production]
    config.sendCacheHeaders_onlyWhenLoginDeniedInBranch = 1
    config.cache_clearAtMidnight = 1
[END]

2. Image Optimization

Configure Image Processing

Install/Settings → Extension Configuration → gfx:

'GFX' => [
    'processor' => 'ImageMagick',
    'processor_path' => '/usr/bin/',
    'processor_effects' => true,
    'processor_enabled' => true,
    'processor_allowTemporaryMasksAsPng' => false,
    'jpg_quality' => 80,
    'webp_enabled' => true,
],

WebP Image Generation

Enable WebP in TypoScript:

lib.contentElement {
    settings {
        media {
            additionalConfig {
                sourceCollection {
                    # WebP format
                    10 {
                        format = webp
                        if.isTrue = {$webpEnabled}
                        media = (min-width: 1px)
                        dataKey = default
                    }
                }
            }
        }
    }
}

Responsive Images in Fluid

<f:image
    image="{file}"
    alt="{file.alternative}"
    width="1200c"
    height="675c"
    cropVariant="default"
    loading="lazy"
    additionalAttributes="{
        fetchpriority: 'high'
    }"
/>

<!-- Or with picture element for WebP -->
<picture>
    <source
        srcset="{f:uri.image(image: file, width: '1200c', height: '675c', fileExtension: 'webp')}"
        type="image/webp">
    <f:image
        image="{file}"
        width="1200c"
        height="675c"
        alt="{file.alternative}"
        loading="lazy"
        fetchpriority="high"
    />
</picture>

Priority Hints for LCP Images

TypoScript for Hero Images:

# Add fetchpriority to first image
lib.heroImage = IMAGE
lib.heroImage {
    file {
        import.data = levelmedia: -1, slide
        treatIdAsReference = 1
        width = 1920
        height = 1080c
    }
    altText.data = levelmedia: -1, slide
    titleText.data = levelmedia: -1, slide
    loading = eager
    params = fetchpriority="high"
}

In Fluid Template:

<!-- Hero/LCP image should NOT be lazy loaded -->
<f:if condition="{iterator.isFirst}">
    <f:then>
        <f:image
            image="{image}"
            width="1920c"
            height="1080c"
            loading="eager"
            additionalAttributes="{fetchpriority: 'high'}"
        />
    </f:then>
    <f:else>
        <f:image
            image="{image}"
            width="1920c"
            height="1080c"
            loading="lazy"
        />
    </f:else>
</f:if>

Image Processing Queue

For large sites, use deferred image processing:

// ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'] = true;
$GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path'] = '/usr/bin/';

// Enable image processing queue
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0664';
$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0775';

3. Critical CSS Implementation

Inline Critical CSS

TypoScript:

page {
    includeCSS {
        main = EXT:your_sitepackage/Resources/Public/Css/main.css
        main.media = print
        main.onload = this.media='all';this.onload=null;
    }

    headerData {
        500 = TEXT
        500.value (
            <style>
                /* Critical CSS - Inline above-the-fold styles */
                body {
                    font-family: Arial, sans-serif;
                    margin: 0;
                    padding: 0;
                }
                .header {
                    background: #333;
                    color: #fff;
                    padding: 1rem;
                }
                .hero {
                    min-height: 400px;
                    background-size: cover;
                }
                /* Add other critical styles */
            </style>
        )
    }
}

Extract Critical CSS (Build Process)

Using Grunt/Gulp:

// gulpfile.js
const critical = require('critical');

gulp.task('critical', () => {
    return critical.generate({
        inline: true,
        base: 'typo3conf/ext/your_sitepackage/',
        src: 'Resources/Public/Html/template.html',
        target: {
            html: 'Resources/Private/Layouts/Page.html',
            css: 'Resources/Public/Css/critical.css'
        },
        width: 1300,
        height: 900,
        minify: true
    });
});

4. PHP Performance Optimization

Enable OPcache

php.ini:

opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1

Upgrade to PHP 8.1+ for JIT compilation:

; Enable JIT
opcache.jit=1255
opcache.jit_buffer_size=100M

Composer Autoloader Optimization

composer dump-autoload --optimize --classmap-authoritative

Preload Critical Files (PHP 7.4+)

opcache-preload.php:

<?php
// Preload TYPO3 core classes
opcache_compile_file(__DIR__ . '/vendor/typo3/cms-core/Classes/Core/Bootstrap.php');
opcache_compile_file(__DIR__ . '/vendor/typo3/cms-frontend/Classes/Controller/TypoScriptFrontendController.php');
// Add more critical files

php.ini:

opcache.preload=/var/www/html/opcache-preload.php
opcache.preload_user=www-data

5. TypoScript Optimization

Reduce TypoScript Complexity

Before (Slow):

# Heavy database queries on every page load
lib.newsLatest = CONTENT
lib.newsLatest {
    table = tx_news_domain_model_news
    select {
        pidInList = 123
        orderBy = datetime DESC
        max = 5
    }
    renderObj = TEXT
    renderObj {
        field = title
        wrap = <li>|</li>
    }
}

After (Optimized with Cache):

lib.newsLatest = COA_INT
lib.newsLatest {
    cache.lifetime = 3600
    10 = CONTENT
    10 {
        table = tx_news_domain_model_news
        select {
            pidInList = 123
            orderBy = datetime DESC
            max = 5
        }
        renderObj = TEXT
        renderObj {
            field = title
            wrap = <li>|</li>
        }
    }
}

Use stdWrap Cache

lib.expensiveOperation = TEXT
lib.expensiveOperation {
    value = Expensive calculation result
    cache {
        key = expensive_op_key
        lifetime = 86400
    }
}

Minimize Database Queries

# Bad: Multiple queries
lib.menu1 = HMENU
lib.menu2 = HMENU
lib.menu3 = HMENU

# Good: Single menu with multiple levels
lib.mainMenu = HMENU
lib.mainMenu {
    1 = TMENU
    2 = TMENU
    3 = TMENU
}

6. Resource Loading Optimization

Preconnect to External Domains

page.headerData {
    10 = TEXT
    10.value (
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link rel="dns-prefetch" href="https://www.googletagmanager.com">
    )
}

Defer Non-Critical JavaScript

page.includeJSFooter {
    analytics = EXT:your_sitepackage/Resources/Public/JavaScript/analytics.js
    analytics.defer = 1

    tracking = EXT:your_sitepackage/Resources/Public/JavaScript/tracking.js
    tracking.async = 1
}

Defer Tracking Scripts

# Move tracking to footer and defer
page.jsFooterInline.900 = TEXT
page.jsFooterInline.900.value (
    // Delay analytics loading
    window.addEventListener('load', function() {
        setTimeout(function() {
            var script = document.createElement('script');
            script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX';
            script.async = true;
            document.head.appendChild(script);
        }, 2000);
    });
)

7. Server Configuration

Enable HTTP/2

Apache (.htaccess):

# Enable HTTP/2 (requires mod_http2)
Protocols h2 h2c http/1.1

Nginx:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    # ... rest of config
}

Enable Gzip/Brotli Compression

Apache:

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>

Nginx:

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_comp_level 6;

# Brotli (if available)
brotli on;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

Browser Caching Headers

TypoScript:

config {
    sendCacheHeaders = 1
    sendCacheHeaders_onlyWhenLoginDeniedInBranch = 1

    additionalHeaders {
        10.header = Cache-Control: max-age=31536000, public
        10.if.isTrue.data = TSFE:type
        10.if.value = 1234
        10.if.equals = 1234
    }
}

Apache (.htaccess):

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

8. Extension Optimization

Disable Unused Extensions

Admin Tools → Extensions → Installed Extensions

Deactivate extensions not in use:

  • Development extensions in production
  • Unused analytics extensions
  • Deprecated functionality

Optimize Extension Loading

ext_localconf.php:

<?php
defined('TYPO3') or die();

// Only load in frontend context
if (TYPO3_MODE === 'FE') {
    // Your frontend-only code
}

// Conditional extension loading
if (\TYPO3\CMS\Core\Utility\GeneralUtility::getApplicationContext()->isProduction()) {
    // Production-only extensions
}

9. Database Optimization

Add Indexes

-- Add indexes to frequently queried tables
ALTER TABLE tx_news_domain_model_news ADD INDEX idx_datetime (datetime);
ALTER TABLE pages ADD INDEX idx_doktype (doktype);
ALTER TABLE tt_content ADD INDEX idx_colPos_pid (colPos, pid);

Enable Query Caching

LocalConfiguration.php:

'SYS' => [
    'caching' => [
        'cacheConfigurations' => [
            'database_query_cache' => [
                'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
                'options' => [
                    'hostname' => '127.0.0.1',
                    'database' => 2,
                ],
            ],
        ],
    ],
],

10. Content Delivery Network (CDN)

Configure CDN for Static Assets

TypoScript:

config {
    absRefPrefix = https://cdn.example.com/
    baseURL = https://cdn.example.com/
}

# Or just for specific file types
[globalString = IENV:REQUEST_URI = *.css]
    config.absRefPrefix = https://cdn.example.com/
[END]

CloudFlare Integration

Install CloudFlare extension:

composer require cloudflare/sdk

Configure automatic cache purging on content changes.

Measurement and Monitoring

Install TYPO3 Performance Monitoring

composer require typo3/cms-lowlevel

Backend → System → DB Check → Database Performance

Real User Monitoring (RUM)

page.jsFooterInline.800 = TEXT
page.jsFooterInline.800.value (
    // Track LCP
    new PerformanceObserver((list) => {
        const entries = list.getEntries();
        const lastEntry = entries[entries.length - 1];
        const lcpValue = Math.round(lastEntry.renderTime || lastEntry.loadTime);

        // Send to analytics
        gtag('event', 'web_vitals', {
            'metric_name': 'LCP',
            'metric_value': lcpValue,
            'metric_rating': lcpValue < 2500 ? 'good' : 'poor'
        });
    }).observe({type: 'largest-contentful-paint', buffered: true});
)

Testing LCP Improvements

Tools

  1. PageSpeed Insights: https://pagespeed.web.dev/
  2. WebPageTest: https://www.webpagetest.org/
  3. Chrome DevTools: Performance tab
  4. Lighthouse: Built into Chrome DevTools

TYPO3-Specific Testing

  1. Clear all caches: ./vendor/bin/typo3 cache:flush
  2. Test as anonymous user (logged out of backend)
  3. Test with production context: TYPO3_CONTEXT=Production
  4. Monitor with TYPO3 Admin Panel (if enabled)

Common TYPO3 LCP Issues

Issue Cause Solution
Slow TTFB PHP/Database Enable caching, optimize queries
Large images Unoptimized FAL Enable WebP, set proper sizes
Render-blocking CSS CSS in head Extract critical CSS
Heavy TypoScript Complex parsing Simplify, cache results
Extension overhead Too many extensions Disable unused extensions

Next Steps