Sitecore Multi-site Multi-CDN implementation

Recently we have been going through a series of SEO tweaks based on recommendations from our SEO Expert. Several of them are very interesting to look into and the reasoning and how search engines rank sites based on that is amazing.

One of those happens to be how we use a CDN. CDN stands for Content Delivery Network. A CDN helps deliver anything from web page assets to the page itself to the end users browser. In our case we use a CDN for our website assets like CSS, java-script and media library items.

Traditionally we set the following configuration in Sitecore:

     
    
      
        public
      
      
        false
      
      
        https://SOMENAME.azureedge.net/
      
    
    

This works fine as the server url is added to all the media library links. Now, if we have multiple sites, we would want to control the CDN accordingly. Also based on the SEO guidelines its better to do cdn.SiteA.com or anysubdomain.SiteA.com for the CDN name.

So now we need to use cdn.SiteA.com and cdn.SiteB.com on the same instance of Sitecore where we have only one setting which is Media.MediaLinkServerUrl. To get around this we turn to the MediaProvider. We would need to override the GetMediaUrl function to append the appropriate CDN host based on the website.

There are a few CDN modules on the Sitecore marketplace but we wanted something simple. We want it to just modify the Media Library links and append the url of the CDN per site. To do this, we add an additional attribute to the site node of your website to store the CDN hostname.

     

  
    
      
      
      
      
    
  

    

Once that is done you would need to create the following class called CDNMediaProvider inheriting from the MediaProvider class.

  
using Sitecore.Data.Items;
using Sitecore.Resources.Media;
using System;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;
using Sitecore;
using Sitecore.Sites;
using Sitecore.Text;
using Sitecore.Web;
using YOURSOLUTION.Business.Caching;
using YOURSOLUTION.SCExtensions.Settings;

namespace YOURSOLUTIONNAMESPACE.Business.SC.Providers
{
    public class CDNMediaProvider : MediaProvider
    {
        string CDNHostnamePropertyKey = "";

        public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
        {
            string baseMediaUrl = base.GetMediaUrl(item, options);
            IGeneralSettings settings = GeneralSettings.Current;
            CDNHostnamePropertyKey = settings.CDNHostnameKey;

            if (HasCDN(Sitecore.Context.Site))
            {
                string cacheKey = string.Concat(Sitecore.Context.Site.Name, baseMediaUrl);

                var cache = ServiceLocator.ServiceProvider.GetService();
                if (cache != null)
                {
                    baseMediaUrl = cache.GetOrCreateItem(cacheKey, () => GetMediaUrl(item, baseMediaUrl),
                        new TimeSpan(6, 0, 0));
                }
            }
            return baseMediaUrl;
        }

        private string GetMediaUrl(MediaItem item, string baseMediaUrl)
        {
            UrlString url = new UrlString(baseMediaUrl);

            if (baseMediaUrl.Contains(Sitecore.Configuration.Settings.Media.MediaLinkPrefix))
            {
                var version = item.InnerItem.Version.Number.ToString();
                var updated = DateUtil.ToIsoDate(item.InnerItem.Statistics.Updated);

                url.HostName = Sitecore.Context.Site.Properties[CDNHostnamePropertyKey];
                url.Protocol = WebUtil.GetScheme();
                url.Add("v", version);
                url.Add("d", updated);
                baseMediaUrl = url.ToString();
            }
            return LinkHelper.RemoveSslPort(baseMediaUrl);
        }

        private bool HasCDN(SiteContext siteContext)
        {
            return !string.IsNullOrEmpty(siteContext.Properties[CDNHostnamePropertyKey]);
        }
    }
}

The code above can serve requests as they come and cache them for a speedy retrieval. As you can see I am adding in the version and the updated date so that published changes will get reflected. To get this code executing we need to register our CDNMediaProvider class using the following patch config.

     

  
    
      
        YOURSOLUTIONNAMESPACE.Business.SC.Providers.CDNMediaProvider, YOURSOLUTIONNAMESPACE.Business
      
    
  

That is it. Once the code is published with the right settings, you will see your media urls come up with the cdn host appended.
https://cdn.firstsitedomain.com/-/media/hero/blackboard.png?v=1&d=20170322T200703Z

If you have any questions or concerns, please get in touch with me. (@akshaysura13 on twitter or on Slack).

3 thoughts on “Sitecore Multi-site Multi-CDN implementation”

  1. Hey Akshay,

    I’m curious why you are using Service Locator in your class CDNMediaProvider. Couldn’t you just inject your ICacheLogic instance into the constructor of this class?

    Cheers,

    Mike

  2. Hey Akshay, please can you tell me how can we send assets to CDN server. I believe we have to override publish event as well which will push the assets to the CDN server. Please, can you provide me some information around this?
    Thanks, Jitendra

Leave a Reply

Your email address will not be published. Required fields are marked *