Sitecore LCP Optimization
General Guide: See LCP (Largest Contentful Paint) for universal concepts and fixes.
Learn how to optimize Largest Contentful Paint (LCP) in Sitecore sites through advanced caching strategies, image optimization, ASP.NET performance tuning, and Sitecore-specific optimizations.
Understanding LCP in Sitecore
Largest Contentful Paint measures how long it takes for the largest content element to render. In Sitecore, this is often affected by:
- .NET/ASP.NET server-side rendering
- Sitecore HTML caching configuration
- Media Library image processing
- Razor view compilation
- xDB tracking initialization
- Component rendering pipeline
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 Sitecore LCP Elements
- Hero images in Razor layouts
- Banner images from Media Library
- Featured content images
- Product images (Sitecore Commerce)
- Background images in components
1. Sitecore HTML Caching
Enable HTML Cache
Via Sitecore UI:
- Navigate to rendering definition
- Check Cacheable option
- Set Cache options (VaryByData, VaryByUser, etc.)
Via Configuration:
<!-- App_Config/Include/YourSite/Caching.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<caching>
<cache name="html" enabled="true">
<patch:attribute name="maxSize">500MB</patch:attribute>
</cache>
</caching>
</sitecore>
</configuration>
Configure Rendering Cache
Set cache options in rendering definition:
<rendering
cacheable="true"
varyByData="true"
varyByDevice="false"
varyByLogin="false"
varyByParm="false"
varyByQueryString="false"
varyByUser="false">
<cacheKey>MyRendering_$(datasource)</cacheKey>
</rendering>
Programmatically in Razor:
@using Sitecore.Mvc.Analytics.Extensions
@{
Html.Sitecore().CachingPolicy(p =>
{
p.VaryByData();
p.SetCacheable();
});
}
Clear HTML Cache Strategically
// Clear specific site HTML cache
var site = Sitecore.Configuration.Factory.GetSite("website");
var cache = Sitecore.Caching.CacheManager.GetHtmlCache(site);
cache.Clear();
// Clear all HTML caches
foreach (var site in Sitecore.Configuration.Factory.GetSiteNames())
{
var siteObj = Sitecore.Configuration.Factory.GetSite(site);
if (siteObj != null)
{
Sitecore.Caching.CacheManager.GetHtmlCache(siteObj).Clear();
}
}
2. Sitecore Media Library Optimization
Configure Image Processing
In web.config or patch file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<settings>
<!-- Enable media caching -->
<setting name="Media.CacheEnabled" value="true"/>
<!-- Set max media cache size -->
<setting name="Media.MaxSizeInCacheKB" value="5120"/>
<!-- Enable media response caching -->
<setting name="Media.ResponseCaching.Enabled" value="true"/>
<setting name="Media.ResponseCaching.MaxAge" value="31536000"/>
<!-- Image quality settings -->
<setting name="Media.DefaultJpegQuality" value="80"/>
<setting name="Media.DefaultPngQuality" value="80"/>
</settings>
</sitecore>
</configuration>
Use Responsive Images
Razor implementation:
@using Sitecore.Resources.Media
@{
var imageItem = (Sitecore.Data.Items.MediaItem)Sitecore.Context.Database.GetItem(Model.Item["Image"]);
if (imageItem != null)
{
var mediaOptions = new MediaUrlBuilderOptions
{
Width = 1920,
Height = 1080,
DisableMediaCache = false
};
var imageUrl = MediaManager.GetMediaUrl(imageItem, mediaOptions);
<picture>
<source media="(min-width: 1200px)"
srcset="@HashingUtils.ProtectAssetUrl(MediaManager.GetMediaUrl(imageItem, new MediaUrlBuilderOptions { Width = 1920 }))">
<source media="(min-width: 768px)"
srcset="@HashingUtils.ProtectAssetUrl(MediaManager.GetMediaUrl(imageItem, new MediaUrlBuilderOptions { Width = 1200 }))">
<source media="(min-width: 480px)"
srcset="@HashingUtils.ProtectAssetUrl(MediaManager.GetMediaUrl(imageItem, new MediaUrlBuilderOptions { Width = 768 }))">
<img src="@HashingUtils.ProtectAssetUrl(imageUrl)"
alt="@imageItem.Alt"
width="1920"
height="1080"
loading="eager"
fetchpriority="high" />
</picture>
}
}
Priority Hints for LCP Images
For hero/above-the-fold images:
@{
var heroImage = (Sitecore.Data.Items.MediaItem)Sitecore.Context.Database.GetItem(Model.Item["Hero Image"]);
if (heroImage != null)
{
var imageUrl = MediaManager.GetMediaUrl(heroImage, new MediaUrlBuilderOptions { Width = 1920, Height = 1080 });
<img src="@HashingUtils.ProtectAssetUrl(imageUrl)"
alt="@heroImage.Alt"
width="1920"
height="1080"
loading="eager"
fetchpriority="high" />
}
}
For below-the-fold images:
<img src="@imageUrl"
alt="@alt"
loading="lazy" />
Media CDN Configuration
Configure CDN for media:
<setting name="Media.MediaLinkServerUrl" value="https://cdn.yoursite.com"/>
Or in Razor:
@{
var cdnUrl = Sitecore.Configuration.Settings.GetSetting("Media.CDN.Url");
var mediaUrl = MediaManager.GetMediaUrl(imageItem);
var fullUrl = $"{cdnUrl}{mediaUrl}";
}
Advanced Image Optimization
Use Sitecore Image Resizer with Quality Settings:
@using Sitecore.Resources.Media
@{
var heroImage = (Sitecore.Data.Items.MediaItem)Sitecore.Context.Database.GetItem(Model.Item["Hero Image"]);
if (heroImage != null)
{
var mediaOptions = new MediaUrlBuilderOptions
{
Width = 1920,
Height = 1080,
Quality = 85,
Format = "jpg",
AllowStretch = false,
DisableMediaCache = false,
IncludeExtension = true
};
var imageUrl = MediaManager.GetMediaUrl(heroImage, mediaOptions);
<img src="@HashingUtils.ProtectAssetUrl(imageUrl)"
alt="@heroImage.Alt"
width="1920"
height="1080"
loading="eager"
fetchpriority="high"
decoding="async" />
}
}
WebP Format Support:
@{
var image = (Sitecore.Data.Items.MediaItem)Sitecore.Context.Database.GetItem(Model.Item["Image"]);
if (image != null)
{
var webpUrl = MediaManager.GetMediaUrl(image, new MediaUrlBuilderOptions
{
Width = 1920,
Format = "webp",
Quality = 80
});
var jpgUrl = MediaManager.GetMediaUrl(image, new MediaUrlBuilderOptions
{
Width = 1920,
Format = "jpg",
Quality = 85
});
<picture>
<source srcset="@HashingUtils.ProtectAssetUrl(webpUrl)" type="image/webp">
<source srcset="@HashingUtils.ProtectAssetUrl(jpgUrl)" type="image/jpeg">
<img src="@HashingUtils.ProtectAssetUrl(jpgUrl)"
alt="@image.Alt"
width="1920"
height="1080"
loading="eager"
fetchpriority="high" />
</picture>
}
}
3. ASP.NET Performance Optimization
Enable Output Caching
In web.config:
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="SitecorePages"
duration="3600"
varyByParam="*"
location="Server" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
In Razor view:
@{
if (!Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.Now.AddHours(1));
Response.Cache.SetValidUntilExpires(true);
}
}
Enable View State Compression
<system.web>
<pages enableViewStateMac="false"
viewStateEncryptionMode="Never"
controlRenderingCompatibilityVersion="4.0">
<namespaces>
<add namespace="Sitecore.Mvc"/>
</namespaces>
</pages>
</system.web>
Razor View Compilation
Precompile views for production:
<system.web>
<compilation debug="false"
targetFramework="4.8"
batch="true"
optimizeCompilations="true">
</compilation>
</system.web>
Use Razor Generator for ahead-of-time compilation:
# Install Razor Generator
Install-Package RazorGenerator.Mvc
4. IIS and .NET Optimization
Enable HTTP Compression
In web.config:
<system.webServer>
<httpCompression>
<staticTypes>
<add mimeType="text/*" enabled="true"/>
<add mimeType="message/*" enabled="true"/>
<add mimeType="application/javascript" enabled="true"/>
<add mimeType="application/json" enabled="true"/>
<add mimeType="*/*" enabled="false"/>
</staticTypes>
<dynamicTypes>
<add mimeType="text/*" enabled="true"/>
<add mimeType="message/*" enabled="true"/>
<add mimeType="application/x-javascript" enabled="true"/>
<add mimeType="*/*" enabled="false"/>
</dynamicTypes>
</httpCompression>
<urlCompression doStaticCompression="true" doDynamicCompression="true"/>
</system.webServer>
Enable Static Content Caching
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00"/>
</staticContent>
</system.webServer>
HTTP/2 Configuration
Ensure HTTP/2 is enabled in IIS:
- Open IIS Manager
- Select server node
- Open Configuration Editor
- Navigate to
system.webServer/serverRuntime - Set
enabledtotrue
5. Sitecore-Specific Optimizations
Disable xDB for Performance Pages
For landing pages without personalization:
<setting name="Xdb.Tracking.Enabled" value="false" patch:source="DisableTracking.config"/>
Or conditionally in code:
if (ShouldDisableTracking())
{
Sitecore.Analytics.Tracker.Current?.Session?.Interaction?.Close();
}
Optimize Component Rendering
Cache expensive operations:
// Cache component data
public ActionResult MyComponent()
{
var cacheKey = $"MyComponent_{Sitecore.Context.Item.ID}";
var cache = Sitecore.Caching.CacheManager.GetNamedInstance<object>("MyCustomCache", StringUtil.ParseSizeString("10MB"));
var data = cache.GetValue(cacheKey);
if (data == null)
{
data = GetExpensiveData();
cache.Add(cacheKey, data, TimeSpan.FromHours(1));
}
return View(data);
}
Limit Database Calls
Bad (Multiple database calls):
@foreach (var child in Model.Item.Children)
{
var title = child["Title"];
var description = child["Description"];
var image = child["Image"];
}
Good (Single call with field caching):
@{
var children = Model.Item.Children.ToList();
foreach (var child in children)
{
child.Fields.ReadAll(); // Load all fields at once
var title = child["Title"];
var description = child["Description"];
var image = child["Image"];
}
}
Use Fast Query for Lists
// Fast query (more performant)
var items = Sitecore.Context.Database.SelectItems($"fast:/sitecore/content/Home//*[@@templateid='{templateId}']");
// Better: Use Content Search API
var index = ContentSearchManager.GetIndex("sitecore_web_index");
using (var context = index.CreateSearchContext())
{
var results = context.GetQueryable<SearchResultItem>()
.Where(x => x.TemplateId == templateId)
.ToList();
}
6. Database and Index Optimization
SQL Server Optimization
Enable connection pooling:
<connectionStrings>
<add name="core"
connectionString="Data Source=...;Min Pool Size=5;Max Pool Size=100;Pooling=true" />
<add name="master"
connectionString="Data Source=...;Min Pool Size=5;Max Pool Size=100;Pooling=true" />
<add name="web"
connectionString="Data Source=...;Min Pool Size=5;Max Pool Size=100;Pooling=true" />
</connectionStrings>
Index maintenance:
-- Rebuild indexes regularly
ALTER INDEX ALL ON [Sitecore_Core].[dbo].[Items] REBUILD;
ALTER INDEX ALL ON [Sitecore_Master].[dbo].[Items] REBUILD;
ALTER INDEX ALL ON [Sitecore_Web].[dbo].[Items] REBUILD;
-- Update statistics
UPDATE STATISTICS [Sitecore_Web].[dbo].[Items];
Rebuild Search Indexes
// Rebuild web index for better search performance
var index = ContentSearchManager.GetIndex("sitecore_web_index");
index.Rebuild();
7. CDN Integration
Configure Azure CDN
Step 1: Configure Sitecore for Azure CDN
<!-- App_Config/Include/CDN/AzureCDN.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<settings>
<!-- Set CDN URL for media -->
<setting name="Media.MediaLinkServerUrl" value="https://yourcdn.azureedge.net"/>
<!-- Enable always include server URL -->
<setting name="Media.AlwaysIncludeServerUrl" value="true"/>
<!-- Enable media response caching -->
<setting name="Media.ResponseCaching.Enabled" value="true"/>
<setting name="Media.ResponseCaching.MaxAge" value="31536000"/>
</settings>
</sitecore>
</configuration>
Step 2: Configure Azure CDN Cache Rules
In Azure Portal:
- Navigate to your CDN profile
- Add caching rules:
- Path:
/~/media/* - Cache expiration: 1 year
- Query string caching: Cache every unique URL
- Path:
Step 3: Verify CDN Integration
@using Sitecore.Resources.Media
@{
var image = (Sitecore.Data.Items.MediaItem)Sitecore.Context.Database.GetItem(Model.Item["Image"]);
if (image != null)
{
var imageUrl = MediaManager.GetMediaUrl(image);
// imageUrl should now include CDN domain
}
}
Configure CloudFlare CDN
Step 1: CloudFlare Page Rules
Create page rules in CloudFlare dashboard:
Media Files Cache Rule:
- URL Pattern:
*yoursite.com/-/media/* - Settings:
- Cache Level: Cache Everything
- Edge Cache TTL: 1 year
- Browser Cache TTL: 1 year
- URL Pattern:
Static Assets Cache Rule:
- URL Pattern:
*yoursite.com/dist/* - Settings:
- Cache Level: Cache Everything
- Edge Cache TTL: 1 month
- URL Pattern:
Step 2: CloudFlare Performance Settings
Enable these in CloudFlare Dashboard > Speed:
- Auto Minify (CSS, JS, HTML)
- Brotli compression
- Rocket Loader (test carefully with Sitecore)
- Mirage (image optimization)
- Polish (image compression)
Step 3: Sitecore Configuration for CloudFlare
<!-- App_Config/Include/CDN/CloudFlare.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<settings>
<!-- Enable response caching -->
<setting name="Media.ResponseCaching.Enabled" value="true"/>
<setting name="Media.ResponseCaching.MaxAge" value="31536000"/>
</settings>
</sitecore>
</configuration>
Step 4: Set Cache-Control Headers
In web.config:
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00"/>
</staticContent>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="public, max-age=31536000, immutable"/>
</customHeaders>
</httpProtocol>
</system.webServer>
Custom CDN Implementation
For custom CDN solutions:
// /Pipelines/HttpRequest/CDNMediaProvider.cs
using Sitecore.Pipelines.HttpRequest;
using Sitecore.Resources.Media;
namespace YourProject.Pipelines.HttpRequest
{
public class CDNMediaProvider : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
if (Sitecore.Context.PageMode.IsExperienceEditorEditing)
return;
// Override media URL generation
var cdnUrl = Sitecore.Configuration.Settings.GetSetting("CDN.BaseUrl");
if (!string.IsNullOrEmpty(cdnUrl))
{
// Inject CDN URL logic here
}
}
}
}
Register in config:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor type="YourProject.Pipelines.HttpRequest.CDNMediaProvider, YourProject"
patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
CDN Purging Strategy
Azure CDN Purge:
// Purge CDN cache when media is updated
using Microsoft.Azure.Management.Cdn;
using Microsoft.Azure.Management.Cdn.Models;
public void PurgeMediaFromCDN(string mediaPath)
{
var cdnClient = new CdnManagementClient(credentials);
var purgeParams = new PurgeParameters
{
ContentPaths = new List<string> { mediaPath }
};
cdnClient.Endpoints.PurgeContent(
resourceGroupName: "your-resource-group",
profileName: "your-cdn-profile",
endpointName: "your-endpoint",
contentPaths: purgeParams
);
}
CloudFlare Purge:
// Purge CloudFlare cache
using System.Net.Http;
public async Task PurgeCloudFlareCache(string[] urls)
{
var client = new HttpClient();
var apiToken = Sitecore.Configuration.Settings.GetSetting("CloudFlare.ApiToken");
var zoneId = Sitecore.Configuration.Settings.GetSetting("CloudFlare.ZoneId");
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiToken}");
var content = new StringContent(
Newtonsoft.Json.JsonConvert.SerializeObject(new { files = urls }),
System.Text.Encoding.UTF8,
"application/json"
);
await client.PostAsync(
$"https://api.cloudflare.com/client/v4/zones/{zoneId}/purge_cache",
content
);
}
8. Critical CSS Implementation
Extract and inline critical CSS for above-the-fold content:
@* /Views/Shared/_Layout.cshtml *@
<!DOCTYPE html>
<html>
<head>
<style>
/* Critical CSS - Inline */
body { margin: 0; font-family: Arial, sans-serif; }
.header { background: #333; color: #fff; padding: 1rem; }
.hero { min-height: 500px; background-size: cover; }
/* Add other critical styles */
</style>
@* Load full CSS asynchronously *@
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
</head>
9. Monitoring and Measurement
Application Insights
Configure Application Insights:
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
<TelemetryModules>
<Add Type="Microsoft.ApplicationInsights.DependencyCollector.DependencyTrackingTelemetryModule"/>
<Add Type="Microsoft.ApplicationInsights.Web.RequestTrackingTelemetryModule"/>
<Add Type="Microsoft.ApplicationInsights.Web.ExceptionTrackingTelemetryModule"/>
</TelemetryModules>
</ApplicationInsights>
Track LCP with Real User Monitoring
// Add to layout
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
const lcpValue = Math.round(lastEntry.renderTime || lastEntry.loadTime);
// Send to analytics
if (typeof gtag !== 'undefined') {
gtag('event', 'web_vitals', {
'metric_name': 'LCP',
'metric_value': lcpValue,
'metric_rating': lcpValue < 2500 ? 'good' : 'poor',
'page_template': '@Sitecore.Context.Item.TemplateName'
});
}
}).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
Sitecore-Specific Testing
Clear all caches:
- HTML cache
- Data cache
- Item cache
- Media cache
Test in normal mode (not Experience Editor or Preview)
Test published web database (not master)
Monitor with Sitecore tools:
/sitecore/admin/stats.aspx/sitecore/admin/cache.aspx
Common Sitecore LCP Issues
| Issue | Cause | Solution |
|---|---|---|
| Slow TTFB | .NET processing, database | Enable caching, optimize queries |
| Large images | Unoptimized Media Library | Configure image processing, use CDN |
| Render-blocking CSS | CSS in head | Extract critical CSS |
| Heavy Razor views | Complex view logic | Cache rendering, optimize code |
| xDB overhead | Analytics tracking | Consider disabling for static pages |
| No HTML cache | Cache not configured | Enable and configure HTML cache |
Checklist
- HTML cache enabled for all renderings
- Media response caching enabled
- Images have explicit dimensions
- LCP image uses fetchpriority="high"
- Output caching configured
- HTTP compression enabled
- CDN configured for media
- Critical CSS inlined
- View compilation optimized
- Database indexes rebuilt
- SQL connection pooling enabled
- Application Insights configured