Setup Sitecore Multi-Domain Solution on Azure WAF level






Are you planning to set up the multi-site solution in Sitecore, which needs to configure on Azure WAF level?

The Multi-site solution in Sitecore is a straight forward process and needs some entry in the web.config to resolve the multiple websites, for more information about Sitecore multi-site setup, you can follow this blog:

https://doc.sitecore.com/developers/81/sitecore-experience-platform/en/configure-multiple-managed-websites.html

If you are not using AZURE WAF, then it's straight forward process, you need to add the multiple domain mapping on your Azure CD – App Services

Steps:

  1. Go to the App service – CD
  2. Add the custom domain tab on the left side
  3. Add the custom domain mapping
  4. Add the CNAME to the Azure CD URL




Once you did the above configuration, then we need to add the entry in the Sitecore config file with the site name, and then automatically the multi-site domain will be resolved.

But the real challenge comes, when we are configuring the WAF with Azure PaaS, because now all the requests will come from WAF level, and the custom domain needs to configure/Map on the Azure WAF level,

In this case, a request is coming on WAF first then, CD (App Services) then the web.config( where the domain is resolving in Sitecore)

We have setup multi-domain on Azure WAF using this Microsoft blog: https://docs.microsoft.com/en-us/azure/application-gateway/create-multiple-sites-portal

But the problem is – a custom domain is not passing from Azure WAF to CD server(web apps) that’s the reason it’s not resolving the multi-site domain.

Below is the configuration we did on WAF level:

  1. Azure VNet setup
  2. Azure WAF setup
  3. Domain level changes
  4. WAF - configuring multi-site


Solution:

We did multiple tries to configure the custom domain on WAF level, but somehow not able to resolve the domain on Sitecore config level, then finally we thought about the customization in the Sitecore pipeline:

All the requests in Sitecore are going through the specific pipeline before resolving the Request, so we had to figure out which is the main pipeline where a multi-site domain is resolving, after doing some research we got to know that “SiteResolver” in HttpBegin Pipeline is the right place for this customization.

So now the challenge is to know some identifier of the domain in the headers so that we can resolve the site based on the same header key, so after debugging the headers parameters we found one unique the identifier in below header parameter:

"X-ORIGINAL-HOST"

In this parameter, the domain is coming from WAF, so we put the condition based on the parameter as below:

Below is the code for this logic:

//for custom checking the site domain and resolving the domain
            foreach (SiteInfo info in this.SiteContextFactory.GetSites())
            {
                var originalHost = args.HttpContext.Request.Headers["X-ORIGINAL-HOST"];
                if (!string.IsNullOrEmpty(originalHost) && info.HostName == originalHost)
                {
                    siteContext = new SiteContext(info);
                }
            }

           
Also, below is the complete class code for the same implementation; we have created a custom pipeline called “customSiteResolver.”

public class CustomSiteResolver : SiteResolver
    {
        public CustomSiteResolver() : this(ServiceLocator.ServiceProvider.GetRequiredService<BaseSiteContextFactory>())
        {
        }
        public CustomSiteResolver(BaseSiteContextFactory siteContextFactory) : base(siteContextFactory)
        {
        }
        protected CustomSiteResolver(BaseSiteContextFactory siteContextFactory, bool enableSiteConfigFiles) : base(siteContextFactory, enableSiteConfigFiles)
        {
        }
        public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            SiteContext site = CustomResolveSiteContext(args);
            this.UpdatePaths(args, site);
            this.SetSiteToRequestContext(site);
        }

        protected virtual SiteContext CustomResolveSiteContext(HttpRequestArgs args)
        {
            SiteContext siteContext = null;
            string queryString = this.GetQueryString(this.SiteQueryStringKey, args);
            if (queryString.Length > 0)
            {
                siteContext = this.SiteContextFactory.GetSiteContext(queryString);
                Assert.IsNotNull(siteContext, string.Concat("Site from query string was not found: ", queryString));
                return siteContext;
            }
            if (this.EnableSiteConfigFiles)
            {
                string str = this.ExtractSiteConfigPathForRequestedDirectory(args);
                if (!string.IsNullOrEmpty(str))
                {
                    siteContext = this.SiteContextFactory.GetSiteContextFromFile(str);
                    Assert.IsNotNull(siteContext, string.Concat("Site from site.config was not found: ", str));
                    return siteContext;
                }
            }
            //for custom checking the site domain and resolving the domain
            foreach (SiteInfo info in this.SiteContextFactory.GetSites())
            {
                var originalHost = args.HttpContext.Request.Headers["X-ORIGINAL-HOST"];
                if (!string.IsNullOrEmpty(originalHost) && info.HostName == originalHost)
                {
                    siteContext = new SiteContext(info);
                }
            }
            if (siteContext == null)
            {
                Uri requestUrl = args.RequestUrl;
                siteContext = this.SiteContextFactory.GetSiteContext(requestUrl.Host, args.Url.FilePath, requestUrl.Port);
            }
            return siteContext;
        }

    }

Below is the configuration to patch the above code in the pipeline
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor type="CustomApp.Foundation.CMS.Pipelines.CustomSiteResolver, CustomApp.Foundation.CMS" patch:instead="processor[@type='Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel']" />
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

Note: All custom domain will read from the standard Sitecore multi-website config only.
  
I hope this article will help you.


Happy Sitecoring





Comments

Post a Comment

Popular posts from this blog

Where is the Log File in #Sitecore 9?

Different way to unpublish the item in sitecore

SETTING UP YOUR FIRST SITECORE HELIX APPLICATION FROM SCRATCH (BLANK SOLUTION)