Janaka Fernando
Mar 13, 2013
  14981
(2 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

Petter Klang
Petter Klang Mar 13, 2013 01:41 PM

Easy solution to what can become a big problem.
I like it!

Mar 13, 2013 02:26 PM

Great topic!

Mar 14, 2013 10:42 AM

Yes that is correct Daniel, Mogul SEO Manager does have this canonical function out of the box (http://www.youtube.com/watch?v=zp-cNCgVnN0). It also have alternate language reference, Sitemap.xml functionality, historical URL redirection etc. All this is solved with one add-on.

Mar 14, 2013 12:25 PM

Hi Daniel and Ove, great to hear more about your SEO Manager product. EPiServer are positioning Mogul as a commercial add-on for the solutions you mentioned ( http://www.episerver.com/AddOns/Mogul-SEO-Manager/ )

Any plans on making this available to trial through the add-on store?

Mar 14, 2013 12:56 PM

For EPiServer 6 R2 is already out there. And for Epi 7, it will be in the add-on store shortly.

Mar 14, 2013 03:25 PM

Great! Looking forward to it.

Please login to comment.
Latest blogs
Opti ID overview

Opti ID allows you to log in once and switch between Optimizely products using Okta, Entra ID, or a local account. You can also manage all your use...

K Khan | Jul 26, 2024

Getting Started with Optimizely SaaS using Next.js Starter App - Extend a component - Part 3

This is the final part of our Optimizely SaaS CMS proof-of-concept (POC) blog series. In this post, we'll dive into extending a component within th...

Raghavendra Murthy | Jul 23, 2024 | Syndicated blog

Optimizely Graph – Faceting with Geta Categories

Overview As Optimizely Graph (and Content Cloud SaaS) makes its global debut, it is known that there are going to be some bugs and quirks. One of t...

Eric Markson | Jul 22, 2024 | Syndicated blog

Integration Bynder (DAM) with Optimizely

Bynder is a comprehensive digital asset management (DAM) platform that enables businesses to efficiently manage, store, organize, and share their...

Sanjay Kumar | Jul 22, 2024