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
- Open Chrome DevTools (F12)
- Go to Performance tab
- Click Record and reload page
- Stop recording
- 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:
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,
],
],
],
],
Redis Cache Backend (Recommended)
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
PHP 8+ (Recommended for TYPO3 12+)
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
- PageSpeed Insights: https://pagespeed.web.dev/
- WebPageTest: https://www.webpagetest.org/
- Chrome DevTools: Performance tab
- Lighthouse: Built into Chrome DevTools
TYPO3-Specific Testing
- Clear all caches:
./vendor/bin/typo3 cache:flush - Test as anonymous user (logged out of backend)
- Test with production context:
TYPO3_CONTEXT=Production - 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 |