Exam Area: Area 2 – Installation, Operation & Configuration (20%) Reference: https://docs.developers.optimizely.com/digital-experience-platform/docs/cdn
A Content Delivery Network (CDN) is a geographically distributed network of edge servers that cache content close to end users, reducing latency and origin server load.
CMS 12 uses CDN for:
The Optimizely DXP includes a CDN (based on Akamai) as part of the platform:
User (any country)
↓
CDN Edge Node (nearest location)
↓ (cache hit: serve from edge)
↓ (cache miss: forward to origin)
Origin: Azure App Service (your CMS)
Cache hit ratio for a well-configured site: 80-95%
| Content | Cached by CDN | Notes |
|---|---|---|
| Static files (.js, .css, images) | ✅ Yes | Long cache TTL (1 year with content hash) |
| Blob storage media | ✅ Yes | Based on blob URL TTL |
| HTML pages | ✅ Configurable | Requires cache headers; tricky for personalised content |
| API responses | ✅ Configurable | Must set correct Cache-Control |
| CMS Edit UI (/episerver) | ❌ No | Always served from origin |
// Set cache headers in ASP.NET Core
// Option 1: Output caching attribute
[ResponseCache(Duration = 3600, VaryByHeader = "Accept-Language")]
public IActionResult Index() { ... }
// Option 2: Programmatic
Response.Headers["Cache-Control"] = "public, max-age=3600, s-maxage=86400";
Response.Headers["Vary"] = "Accept-Encoding, Accept-Language";
// Option 3: Output Cache Middleware (ASP.NET Core 7+)
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromMinutes(60)));
});
app.UseOutputCache();
When content is published, the CDN cache for affected URLs should be purged:
// CMS publishes content → trigger CDN purge
// Option 1: Custom event handler
[ServiceConfiguration]
public class CdnPurgeHandler : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
var events = context.Locate.Advanced.GetInstance<IContentEvents>();
events.PublishedContent += OnPublished;
}
private void OnPublished(object sender, ContentEventArgs e)
{
var url = _urlResolver.GetUrl(e.ContentLink);
_cdnClient.Purge(url);
}
public void Uninitialize(InitializationEngine context) { }
}
DXP Portal → CDN → Cache Management
→ Purge by URL: /articles/my-page
→ Purge by tag: content-id-42
→ Purge all: Full cache clear (use sparingly — causes traffic spike)
✅ Use content-hash-named static files (Webpack/Vite)
→ e.g. main.a1b2c3d4.js (1-year cache TTL)
✅ Separate CDN URL for Blob Storage media
→ cdn.mysite.com/media/... → origin: mystorage.blob.core.windows.net/...
✅ Exclude /episerver/* from CDN caching
→ CMS Edit UI must not be cached
✅ Set Vary: Accept-Encoding on all cacheable responses
✅ Use Cache-Control: private for personalised/user-specific content
The Vary header tells the CDN to cache separate versions per header value:
Vary: Accept-Language → separate cache per language
Vary: Accept-Encoding → separate cache per compression (gzip/br)
Vary: Cookie → separate cache per session (avoid for CDN)
Important: If you use Visitor Groups (personalisation), ensure personalised content is not cached by the CDN — it must be served per-user:
// For personalised responses
Response.Headers["Cache-Control"] = "private, no-store";
Or use Edge Side Includes (ESI) to cache the non-personalised parts and ESI-expand personalised regions dynamically.