CDN recommendations
Describes how to use a content delivery network or content distribution network (CDN) within the Optimizely Digital Experience Platform (DXP), and provides general recommendations for how to configure caching specifically for CDN environments.
The purpose of a content delivery network or content distribution network (CDN) is to ensure high availability when serving content to visitors. A CDN consists of a globally distributed network of proxy servers deployed in multiple data centers. CDNs deliver content from the fastest and closest server available.
The CDN has its own URL pointing to the original URL. The visitor navigates to something like www.customer.com. The request goes to the CDN, which in turn goes to the origin URL for non-cached data. The request returns to the CDN, which caches the content.
By reducing the number of server requests to geographically dispersed locations, you can provide and manage more requests from edge servers closest to the visitor location, resulting in a faster browsing experience. The cache hit ratio describes the percentage of requests the CDN could answer from its own cache.
The CDN stores a copy of your objects and, upon request, serves objects from these caches. The CDN servers are designed to cache content according to the cache rules you define in the HTTP cache headers for your web application. It is critical that you set these caching rules correctly for your solution to scale properly.
CDNs and web applications
Setting up a CDN is fairly easy and, even with minor configuration work, you can get better performance. Develop your application with the CDN in mind from the start, and add proper cache headers based on what you want to achieve. This way, when you connect the CDN, it works automatically.
You should control the cache settings in the web application rather than writing overriding rules in the CDN, because this can quickly get quite complex. However, there may be situations where you need rules, such as when you need special configurations, or if you do not want to deploy with these changes.
Evaluate whether you can cache frequently-requested objects. You can store Static objects indefinitely in the caches. For cloud-based solutions, you should ensure to cache (by the intermediary proxies , such as those provided by a CDN) resources like static and resized images, JavaScript and CSS files, and scripts.
In most cases, you can include a version identifier in the path to the resources. If an object changes, the version identifier updates the URL to it, and the change is immediately reflected. The best way to control the cache for assets is to use unique file names for every deployment. For example: use site-1.0.css in the first deploy, and site-1.1.css in the second deploy. This ensures that the CDN uses the latest CSS file right after deployment.
If you cannot use version identifiers for frequently-requested objects, you can set a maximum lifespan for an object. This is one of the values that are typically set in the cache-control. The ordered priority should be:
- Version identifier plus indefinite caching
- No version identifier plus maximum lifespan
Configure cache headers
Using the correct cache headers in your application ensures that you get the most out of the CDN service, regardless of whether you want to just cache static content, or text/HTML. You can use Last-Modified, cache-control settings, ETags, or a combination of these, to determine whether content has changed.
Cache-control
There are many HTTP header settings, but cache-control is one of the most important ones because this setting determines how long a cached object should remain cached.
A CDN monitors the cache headers in the response settings:
- cache-control: no-cache – The CDN will not cache the content.
- cache-control: public – The CDN caches the content.
Note
You must set the cache-control to public (not private, which means that only the browser is allowed to cache the content).
Typical values:
- max-age=$seconds – Determines the lifespan for an object.
- must-revalidate – Tells the cache to strictly follow maximum lifespan information.
- public – Can be cached by intermediaries.
- private – Can be cached, but only by the browser.
Note
Do not set cache to public on page requests if the page has private information, such as a cart page in e-commerce.
Example: Caching an object for a maximum of 20 minutes.
Cache-Control: public, max-age=1200, must-revalidate
Cache static content
Use as long max-age as possible, at least for static content, to minimize the trips to the web server. In web.config, there are specific sections for setting cache headers for static content. You can see an example of settings when you install an Optimizely site through Visual Studio.
The <staticContent> tag and the <clientCache> subtag set cache headers in the HTTP response. In the following example, static files are cached for one day. The <staticContent> section controls caching for the files that are part of the web application code and cannot be changed by editors.
The <caching> tag and its subsettings controls the IIS cache is cached in the web server by IIS. This will not interfere with the cache headers in the HTTP response, so removing the tag will not make a difference to the cache in the CDN.
Example: Configuring cache headers for static content.
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="1.00:00:00" />
</staticContent>
<caching>
<profiles>
<add extension=".gif"
policy="DontCache"
kernelCachePolicy="CacheUntilChange"/>
<add extension=".png"
policy="DontCache"
kernelCachePolicy="CacheUntilChange"/>
<add extension=".js"
policy="DontCache"
kernelCachePolicy="CacheUntilChange"/>
<add extension=".css"
policy="DontCache"
kernelCachePolicy="CacheUntilChange"/>
<add extension=".jpg"
policy="DontCache"
kernelCachePolicy="CacheUntilChange"/>
<add extension=".jpeg"
policy="DontCache"
kernelCachePolicy="CacheUntilChange"/>
</profiles>
</caching>
</system.webServer>
</configuration>
Overriding SetCachePolicy
If you use SetMaxAge, this sets cache-control to public. The following example sets the max-age using a TimeSpan of one hour, which is converted automatically to 3600 seconds.
public class StartPageController : PageControllerBase
{
public ActionResult Index(StartPage currentPage)
{
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(TimeSpan.FromHours(1));
return View(currentPage);
}
}
If you use SetExpires, this sets cache-control to public, and Expires to your selected date and time.
public class StartPageController : PageControllerBase
{
public ActionResult Index(StartPage currentPage)
{
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.Now.AddHours(1));
return View(currentPage);
}
}
Use ETags
The ETag (entity tag) is a part of the HTTP protocol determining cache validation, and is best used for static content, such as files, uploaded assets, and pages without output caching. You should not use ETags for requests to resources composed from dynamic content objects such as a landing page.
You can combine ETags with other settings. You can use both Cache-Control max-age and ETags to fine-tune cache management and optimize performance. You can also use only ETags, if you have specific caching control requirements.
Recommendations
- Static deployed files such as CSS – Set the expire date for as long time interval as possible, then update the URL to include a version number that changes when new static files are uploaded.
- Uploaded assets such as images – ETags are good for this. However, do not set expire times that are longer than the editors can wait for a file to be updated because editors may be confused if they upload a new image with the same name, and the cache still shows the old image.
- Custom API controllers and "normal" HTML – Normally, you should not cache dynamic content, but it may be useful in some cases. Only cache if you do not have personalized content. If an API is cached, make sure all variables are pulled from the URI rather than headers to avoid displaying incorrectly cached information, and test thoroughly before going live. Do not cache administrative or confidential information.
Disable ETags
If you need to disable ETag headers from the HTTP response, do this by adding a setEtag="false" attribute setting to the web.config file under the application root directory, as in the following example.
<configuration>
...
<system.webServer>
...
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="1.00:00:00"
setEtag="false" />
</staticContent>
...
</system.webServer>
...
</configuration>
Cache levels for query strings
The default cache layer setting in DXP for query strings is Standard, meaning that a different resource is delivered each time the query string changes.
SSL requirements
Proper encryption practices are necessary to prevent bad actors from accessing data. The CDN used in DXP includes web encryption based on TLS (Transport Layer Security)/SSL (Secure Sockets Layer) protocols, for communication with other services over HTTP (HTTPS).
The service includes a default certificate provided by Optimizely, but you can replace this with your own. If you replace the certificate, the following requirements apply:
- The certificate must be valid (not expired), issued by a trusted Certificate Authority (not self-signed), and the DNS must be correct.
- The certificate files (certificate and key) should be PEM-encoded.
Versioned URLs for assets
When the application is deployed to DXP environment, there can be some lag-time for assets to update when replacing them in the Optimizely Content Management System (CMS), due to the asset still being cached in CDN or browsers.
One solution to overcome this is versioning URLs to assets. See https://github.com/bjuris/EPiServer.CdnSupport for an (unsupported) example of how to accomplish this.
See also: Cache-Control headers (IETF Tools)
Updated 9 days ago