Take the community feedback survey now.

janaka.fernando
Mar 13, 2013
  9470
(0 votes)

Adding Canonical support to EPiServer sites

Avoiding Duplicate Content

I think everyone loves the Simple Address feature in EPiServer CMS to easily set up friendly URLs.  If you haven't come across it, Simple Address lets you set a simple URL that will come after the site domain.  This is useful if you have a long URL to the page and want to print or send a short URL  - for example a landing page that you want to direct traffic to.

So for the example here I would have two URLs

http://janaka.uk.episerver.com/About-us/News-Events/Press-Releases/Alloy-pays-it-forward/

simply becomes

http://janaka.uk.episerver.com/Forward/

Now while this is great, it produces two unique URLs with identical content... a big no-no with Google page ranking. To avoid getting penalized for this, you need to set the canonical link to let the search engine know which URL is the primary source of this content to index.

Setting a canonical URL is pretty simple, its a single <link> tag that needs to go into your <head> tag.  So for the above example we'd simply need either of the following:

Absolute <link rel="canonical" href="http://janaka.uk.episerver.com/About-us/News-Events/Press-Releases/Alloy-pays-it-forward/" />
or
Relative <link rel="canonical" href="/About-us/News-Events/Press-Releases/Alloy-pays-it-forward/" />

 

Adding Canonicalization Support to EPiServer

So we need to add a canonical link whenever a page has a Simple Address.  I'm using the new Alloy sample code base for my examples here.  I want to modify the <head> section so looking at the existing code this is best achieved through the master page base class - SiteMasterPage

Below is an excerpt from the SiteMasterPage.cs file with my modifications

 

public abstract class SiteMasterPage : System.Web.UI.MasterPage
    {
        protected override void OnLoad(System.EventArgs e)
        {
            base.OnLoad(e);
 
            if (Master != null)
            {
                return;
            }
 
            SetupMetaTags();
            SetupCanonicalURL();
        }
 
        private void SetupCanonicalURL()
        {
            // Add canonical url meta tag for simple address
            if (!string.IsNullOrEmpty(CurrentPage.ExternalURL) &&
                string.Compare(CurrentPage.ExternalURL, 
                 Request.Url.Segments.Last().Replace("/", ""), true) == 0)
            {
                var urlBuilder = new UrlBuilder(CurrentPage.LinkURL);
 
                if (EPiServer.Global.UrlRewriteProvider.ConvertToExternal(urlBuilder,                                                             
                                    CurrentPage.ContentLink,
                                    System.Text.Encoding.Default))
                {
                    string sourceUrl = urlBuilder.Uri.ToString();
                    var canonicalLink = new HtmlLink();
                    canonicalLink.Attributes.Add("rel", "canonical");
                    canonicalLink.Href = sourceUrl;
 
                    Page.Header.Controls.Add(canonicalLink);
                }
            }
     }
}

Some points to note here.  The API provides a property CurrentPage.ExternalURL which will have the simple address.  If this is present I then check if this is the current URL ( as opposed to the long name URL ).  Finally the rest if fairly straightforward, using the UrlBuilder to construct the external link to the page, then writing this to the Link.  Note that in this example I am using relative links, because CurrentPage.LinkURL is relative, but you could build up the full path and use absolute links using Uri.AbsoluteUri.

Language Fallback & Replacement

In addition to the Simple Address, another place to provide automatic support for canonical URLs is when using language replacement or fallback.  In this case the URL will be different depending on the language, but the site will contain duplicated content. 

I've refactored the previous code sample and added a second condition to the SiteMasterPage SetupCanonicalLink method, checking if the current page's language is different from what is expected.  If it's different I load the source content and update the LinkURL to indicate the source language. 

 

private void SetupCanonicalURL()
        {
            // Add canonical url meta tag for simple address
            if (!string.IsNullOrEmpty(CurrentPage.ExternalURL) &&

string.Compare(CurrentPage.ExternalURL,

Request.Url.Segments.Last().Replace("/", ""), true) == 0)

            {
                var urlBuilder = new UrlBuilder(CurrentPage.LinkURL);
                WriteCanonicalLink(urlBuilder);
            }    
 
 
            // Add canonical url meta tag for language fallback/replacement 
            if (CurrentPage.Language != ContentLanguage.PreferredCulture)
            {
                var sourcePage = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IContentRepository>()
                                                                                 .Get<PageData>(CurrentPage.ContentLink, 
                                         new LanguageSelector(CurrentPage.Language.Name));
                var sourceUrl = UriSupport.AddLanguageSelection(sourcePage.LinkURL, CurrentPage.Language.Name);
                var sourceUrlBuilder = new UrlBuilder(sourceUrl);
 
                WriteCanonicalLink(sourceUrlBuilder);
            }
        }
 
        private void WriteCanonicalLink(UrlBuilder url)
        {
            
            if (EPiServer.Global.UrlRewriteProvider.ConvertToExternal(url, CurrentPage.ContentLink, System.Text.Encoding.Default))
            {
                string sourceUrl = url.Uri.ToString();
                var canonicalLink = new HtmlLink();
                canonicalLink.Attributes.Add("rel", "canonical");
                canonicalLink.Href = sourceUrl;
 
                Page.Header.Controls.Add(canonicalLink);
            }
        }

 

Further reading:

http://www.seomoz.org/blog/canonical-url-tag-the-most-important-advancement-in-seo-practices-since-sitemaps

http://www.mattcutts.com/blog/seo-advice-url-canonicalization/

http://support.google.com/webmasters/bin/answer.py?hl=en&answer=139394

Mar 13, 2013

Comments

Please login to comment.
Latest blogs
A day in the life of an Optimizely OMVP - Opticon London 2025

This installment of a day in the life of an Optimizely OMVP gives an in-depth coverage of my trip down to London to attend Opticon London 2025 held...

Graham Carr | Oct 2, 2025

Optimizely Web Experimentation Using Real-Time Segments: A Step-by-Step Guide

  Introduction Personalization has become de facto standard for any digital channel to improve the user's engagement KPI’s.  Personalization uses...

Ratish | Oct 1, 2025 |

Trigger DXP Warmup Locally to Catch Bugs & Performance Issues Early

Here’s our documentation on warmup in DXP : 🔗 https://docs.developers.optimizely.com/digital-experience-platform/docs/warming-up-sites What I didn...

dada | Sep 29, 2025

Creating Opal Tools for Stott Robots Handler

This summer, the Netcel Development team and I took part in Optimizely’s Opal Hackathon. The challenge from Optimizely was to extend Opal’s abiliti...

Mark Stott | Sep 28, 2025

Integrating Commerce Search v3 (Vertex AI) with Optimizely Configured Commerce

Introduction This blog provides a technical guide for integrating Commerce Search v3, which leverages Google Cloud's Vertex AI Search, into an...

Vaibhav | Sep 27, 2025

A day in the life of an Optimizely MVP - Opti Graph Extensions add-on v1.0.0 released

I am pleased to announce that the official v1.0.0 of the Opti Graph Extensions add-on has now been released and is generally available. Refer to my...

Graham Carr | Sep 25, 2025