Posted in: Comments

This topic comes up from time to time in the forums. I don’t find some of the solutions given completely adequate. They usually just makes the URL absolute by appending the current context’s host name, if present. What we should do instead, is to load the SiteDefinition for the content passed in to our method and then load the host from that definition. The site definition can contain multiple hosts and different kinds as well, e.g. primary host.

This method also works for MediaData.

public static string ContentExternalUrl(this ContentReference contentLink, CultureInfo contentLanguage, bool absoluteUrl)
    var result = ServiceLocator.Current.GetInstance<UrlResolver>().GetUrl(
        new VirtualPathArguments
            ContextMode = ContextMode.Default,
            ForceCanonical = absoluteUrl

    // HACK: Temprorary fix until GetUrl and ForceCanonical works as expected,
    // i.e returning an absolute URL even if there is a HTTP context that matches the content's site definition and host.
    if (absoluteUrl)
        Uri relativeUri;

        if (Uri.TryCreate(result, UriKind.RelativeOrAbsolute, out relativeUri))
            if (!relativeUri.IsAbsoluteUri)
                var siteDefinitionResolver = ServiceLocator.Current.GetInstance<SiteDefinitionResolver>();
                var siteDefinition = siteDefinitionResolver.GetDefinitionForContent(contentLink, true, true);
                var hosts = siteDefinition.GetHosts(contentLanguage, true).ToList();

                var host = hosts.FirstOrDefault(h => h.Type == HostDefinitionType.Primary) 
                    ?? hosts.FirstOrDefault(h => h.Type == HostDefinitionType.Undefined);

                var basetUri = siteDefinition.SiteUrl;

                if (host != null && host.Name.Equals("*") == false)
                    // Try to create a new base URI from the host with the site's URI scheme. Name should be a valid
                    // authority, i.e. have a port number if it differs from the URI scheme's default port number.
                    Uri.TryCreate(siteDefinition.SiteUrl.Scheme + "://" + host.Name, UriKind.Absolute, out basetUri);

                var absoluteUri = new Uri(basetUri, relativeUri);

                return absoluteUri.AbsoluteUri;

    return result;