Came across a weird issue with a client's Sitecore instance recently where on CD instances, internal link URLs generated in Navigation or redirects are absolute URLs including hostnames but they are CM hostname instead of CDs'.

The Sitecore version is 10.1. SXA and Commerce Storefront. On Docker and AKS.

After lots of digging and debugging Sitecore DLLs within docker containers, found the issue and another finding.

  1. CD generates URLs with CM hostname is because SXA site settings don't have a language set


Debugging through the code of LinkManager.GetItemUrl, it reaches Sitecore.Links.UrlBuilders.Helpers.OptionsDecoratedSiteResolver.ResolveSite at some point like below:

// Sitecore.Links.UrlBuilders.Helpers.OptionsDecoratedSiteResolver 
using Sitecore.Data.Items; 
using Sitecore.Diagnostics; 
using Sitecore.Web; 
/// <inheritdoc /> 
/// <summary> 
/// Resolves the site that best matches the given item. 
/// </summary> 
/// <param name="item">Requested item that we need to resolve matching site.</param> 
/// <returns></returns> 
public SiteInfo ResolveSite(Item item) 
Assert.ArgumentNotNull(item, "item"); 
SiteInfo defaultSiteInfo = GetDefaultSiteInfo(); 
if (SkipResolving(item)) 
return defaultSiteInfo; 
return _realSiteResolver.ResolveSite(item) ?? defaultSiteInfo; 
}

if SkipResolving is true, it takes site context from the default which is correct.

However, it is false in this case and it goes to SiteResolver to resolve the site via item only. So there is a chance that the item is resolved to the wrong site since both CM and CD are pointing to the same Sitecore item path and who gets resolved is all dependent on the order of the sites.

But why SkipResolving is false?

// Sitecore.Links.UrlBuilders.Helpers.OptionsDecoratedSiteResolver 
using Sitecore.Data.Items; 
private bool SkipResolving(Item item) 
if (!NoNeedToResolveSite(item) && !OptionsSiteIsDifferentThanContextSite()) 
return ItemMatchesCurrentSite(item); 
return true; 
}
.....
protected virtual bool ItemMatchesCurrentSite(Item item)
{
if (!Settings.Rendering.SiteResolvingMatchCurrentSite)
{
return false;
}
if (PathMatchesContextSite(item))
{
return LanguageMatchesContextSite(item);
}
return false;
}
.....
private bool LanguageMatchesContextSite(Item item)
{
string name = item.Language.Name;
string language = ContextSite.Language;
if (Settings.Rendering.SiteResolvingMatchCurrentLanguage)
{
return name.Equals(language, StringComparison.InvariantCultureIgnoreCase);
}
return true;
}

Out of all the code here, LanguageMatchesContextSite is false because of ContextSite.Language is empty but item.Language.Name is "en".

It's all because "Language" field of site settings item(such as "/sitecore/content/tenant/Sites/siteA/Settings/Site Grouping/siteA-CM") is blank... After setting this field to "en", the issue is no longer presented.

  1. Must set "TargetHostname" if "Hostname" contains pipes or wildcards in SXA site settings


This one was not causing a direct issue however could potentially be one. The GetTargetHostName method on SiteInfo is like below:

// Sitecore.Web.SiteInfo
/// <summary>
/// Gets the name of the target host.
/// </summary>
internal virtual string GetTargetHostName()
{
if (!string.IsNullOrEmpty(TargetHostName))
{
return TargetHostName;
}
if (HostName.IndexOfAny(new char[2]
{
'*',
'|'
}) < 0)
{
return HostName;
}
return string.Empty;
}

It's easy to see that if Hostname contains any wildcards or pipes and TargetHostName is blank, it would just return an empty string. Which would definitely cause issues somewhere on the site.