Search engine optimization

Search engine optimization (SEO) is a process that attempts to improve the page rank of a website. The rank determines the site’s organic position in the results of web search engines (such as Google). Being higher in search results benefits the site by naturally attracting more visitors.

Tracking search engine traffic

You can use web analytics to review the results of your website’s SEO. Tracking is supported for:

  • Traffic gained from search engines
  • The activity of web crawlers on your site’s pages

See: Monitoring traffic from search engines

Implementing sitemap generators

Sitemaps are crucial for SEO as they provide a hierarchical structure of a website’s pages and may also contain metadata such as priority and frequency of changes. This information is used by search engine crawlers to better navigate a site and ensure all pages are discovered and indexed. This leads to more accurate indexing, prioritization of important pages, and ultimately better visibility in search engine results pages. The result of a good sitemap is increased traffic to the site.

Sample sitemap generator

Note: This is a sample implementation of a sitemap generator. This implementation is not a catch-all solution, and depending on your project, you may need to adjust the implementation to suit your needs.

This example of a sitemap generator works by creating a special page type that is then used as a parent for page types that you want to be included in the sitemap. All pages of included page types are then retrieved and returned in the form of a sitemap.

Create the special page type and set up the inheritance in the administration interface:

  1. Navigate to the Page types application and create a new page type.
    1. Set the following properties:
      • Display name: Sitemap selection
      • Namespace: Custom
      • Code name: Sitemap
    2. Enable the following page type features: Page builder, URL, Metadata.
    3. Add a field with the following properties:
      • Field name: IncludeInSitemap
      • Data type: Boolean (Yes/No)
      • Default value:
        • Yes – if you want pages to be included by default
        • No – if you want to manually include pages in the sitemap
      • Field caption: Include page in the sitemap
  2. Define page types that you want to include in the site map to inherit from the newly created page type:
    1. In the Page types application, navigate to each page type you want to include.
    2. Edit the page type. On the General tab, select the Sitemap selection page type in the Inherits fields from page type field.
      • Note that page types that are Navigation items cannot inherit from other page types, but these are included in the sitemap by default.

Open your live site project in Visual Studio:

  1. Install a required NuGet package:

    1. Select Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution…
    2. Search and install the SimpleMvcSitemap package.
  2. Add an endpoint in the project’s startup file (Startup.cs by default):

    Project startup file
    
    
    
     public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
     {
         app.UseEndpoints(endpoints =>
         {
             endpoints.Kentico().MapRoutes();
    
             ...
    
             // Maps custom endpoint for the sitemap generator
             endpoints.MapControllerRoute(
                 name: "SiteMap",
                 pattern: "sitemap.xml",
                 defaults: new { controller = "Sitemap", action = "Index" }
             );
         });
     }
    
    
     
  3. Create a new controller in your project (named SitemapController.cs):

    SitemapController.cs
    
    
    
     using System;
     using System.Collections.Generic;
     using System.Linq;
     using Microsoft.AspNetCore.Mvc;
    
     using CMS.DataEngine;
     using CMS.DocumentEngine;
     using CMS.SiteProvider;
     using Kentico.Content.Web.Mvc;
    
     using SimpleMvcSitemap;
    
     namespace DancingGoat.Controllers
     {
         public class SitemapController : Controller
         {
             private readonly IPageRetriever pageRetriever;
             private const string sitemapClassName = "Custom.Sitemap";
    
             // Gets the page retriever using dependency injection
             public SitemapController(IPageRetriever pageRetriever)
             {
                 this.pageRetriever = pageRetriever ?? throw new ArgumentNullException(nameof(pageRetriever));
             }
    
             // The Index method creates the sitemap - this method is called by the endpoint added in the Startup.cs file
             public ActionResult Index()
             {
                 List<SitemapNode> nodes = new List<SitemapNode>();
                 foreach (var doc in GetXMLSiteMapDocuments())
                 {
                     nodes.Add(new SitemapNode(doc.NodeAliasPath)
                     {
                         // The ToLocalTime function is important - web crawler will not accept the date format without it
                         LastModificationDate = (doc.DocumentModifiedWhen.ToLocalTime())
                     });
                 }
                 return new SitemapProvider().CreateSitemap(new SitemapModel(nodes));
             }
             private IEnumerable<TreeNode> GetXMLSiteMapDocuments()
             {
                 // Defines sitewide variables
                 var culture = SiteContext.CurrentSite.DefaultVisitorCulture;
                 var siteName = SiteContext.CurrentSiteName;
                 // Retrieves the Sitemap selection page type
                 int myBaseClassID = DataClassInfoProvider.GetDataClassInfo(sitemapClassName).ClassID;
                 // Retrieves all page types that inherit from the Sitemap selection page type
                 var AllInheritedClassNames = DataClassInfoProvider
                     .GetClasses()
                     .OnSite(siteName)
                     .WhereEquals("ClassInheritsFromClassID", myBaseClassID)
                     .Select(x => x.ClassName)
                     .ToArray();
                 // Retrieves all the page types that are Navigation Items - they cannot inherit from another page type
                 var NavigationClassNames = DataClassInfoProvider
                     .GetClasses()
                     .OnSite(siteName)
                     .WhereEquals("ClassIsNavigationItem", 1)
                     .Columns("ClassName")
                     .Select(x => x.ClassName)
                     .ToArray();
                 // Queries all inherited pages for the IncludeInSitemap field set to true and caches the result
                 IEnumerable<TreeNode> AllInheritedPages = pageRetriever.RetrieveMultiple(query => query
                         .OnSite(siteName)
                         .Published(true)
                         .PublishedVersion(true)
                         .Path("/%")
                         .WhereEquals("IncludeInSitemap", 1)
                         .Columns("DocumentModifiedWhen, NodeAliasPath")
                         .Types(AllInheritedClassNames),
                     cache => cache
                         .Key($"node|{siteName}|/|inheritedchildnodes")
                         .Dependencies((_, builder) => builder.PagePath("/", PathTypeEnum.Children).PageOrder())
                         .Expiration(TimeSpan.FromMinutes(240))
                     );
                 // Queries all navigation pages and caches the result
                 IEnumerable<TreeNode> NavigationPages = pageRetriever.RetrieveMultiple(query => query
                         .OnSite(siteName)
                         .Published(true)
                         .PublishedVersion(true)
                         .Path("/%")
                         .Columns("DocumentModifiedWhen, NodeAliasPath")
                         .Types(NavigationClassNames),
                     cache => cache
                         .Key($"node|{siteName}|/|navigationchildnodes")
                         .Dependencies((_, builder) => builder.PagePath("/", PathTypeEnum.Children).PageOrder())
                         .Expiration(TimeSpan.FromMinutes(240))
                     );
                 // Returns combined data from both queries
                 return AllInheritedPages.Concat(NavigationPages);
             }
         }
     }
    
    
     

You can now navigate to <domain>/sitemap.xml and see the generated sitemap.