November Happy Hour will be moved to Thursday December 5th.

Janaka Fernando
Mar 13, 2013
  15247
(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
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog