---
title: Caching the output of personalized content
related:
  - https://docs.kentico.com/13/configuring-xperience/configuring-caching/caching-on-mvc-sites.md
  - https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/content-personalization.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

When using [ASP.NET output caching](https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/controllers-and-routing/improving-performance-with-output-caching-cs) to improve the performance of your MVC website, you need to take additional steps to ensure that the output cache correctly stores personalized pages. Because personalized pages serve different content based on various conditions, you need to adjust your application to cache separate output versions according to the [personalization criteria](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/content-personalization.md).

To cache different versions of content, use the [OutputCache](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.outputcacheattribute) attribute together with a custom implementation of the [GetVaryByCustomString](https://docs.microsoft.com/en-us/dotnet/api/system.web.httpapplication.getvarybycustomstring) method.

## Configuring output caching using custom strings

Xperience offers a fluent API built around .NET framework's _VaryByCustomString_ output caching feature that allows you to construct output cache keys according to your project's caching requirements. The system provides an **IOutputCacheKeyOptions** interface that allows you to create configurable objects for building output cache keys. The options object is configured by chaining any number of VaryBy methods (representing cache key parts). The complete cache key is then constructed by calling **OutputCacheKeyHelper.GetVaryByCustomString** with the options object as the parameter.

### VaryBy methods

By default, the system allows you to construct the cache key using the following request variables (extension methods for _IOutputCacheKeyOptions_ objects):

| Method              | Namespace                       | Cache key                                                                                                                                                                                                                                                                                                            |
| ------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| VaryByBrowser       | Kentico.Web.Mvc                 | The name and the major version of the client's browser.                                                                                                                                                                                                                                                              |
| VaryByHost          | Kentico.Web.Mvc                 | The [host](https://docs.microsoft.com/en-us/dotnet/api/system.uri.host) name of the server.                                                                                                                                                                                                                          |
| VaryBySite          | Kentico.Web.Mvc                 | The code name of the site.                                                                                                                                                                                                                                                                                           |
| VaryByUser          | Kentico.Web.Mvc                 | The name of the current user.                                                                                                                                                                                                                                                                                        |
| VaryByCookieLevel   | Kentico.Web.Mvc                 | The [cookie level](https://docs.kentico.com/13/developing-websites/working-with-cookies.md) of the current user.                                                                                                                                                                                                     |
| VaryByPersona       | Kentico.OnlineMarketing.Web.Mvc | The ID of the [persona](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/personas.md) assigned to the current [contact](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/contact-management/working-with-contacts.md). |
| VaryByABTestVariant | Kentico.OnlineMarketing.Web.Mvc | The GUID of the [A/B test variant](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/a-b-testing-pages.md) assigned for the current request.                                                                                                                           |

### Example – Configuring output caching

The following steps describe how to cache multiple versions of personalized output using custom strings:

1. Edit your project's **Global.asax.cs** file and override the [GetVaryByCustomString](https://docs.microsoft.com/en-us/dotnet/api/system.web.httpapplication.getvarybycustomstring) method.

   - The method's _custom_ parameter contains the string that you set in the _VaryByCustom_ property of _OutputCache_ attributes in your controllers.
   - For each custom string, you need to define the caching requirements – use _VaryBy_ methods on an _IOutputCacheKeyOptions_ object to create a set of request variables according to which the output cache needs to vary.

     ```csharp

     using System;
     using System.Web;

     using Kentico.Web.Mvc;
     using Kentico.OnlineMarketing.Web.Mvc;

     ```

     ```csharp title="Example"

             public override string GetVaryByCustomString(HttpContext context, string custom)
             {
                 // Creates the options object used to store individual cache key parts
                 IOutputCacheKeyOptions options = OutputCacheKeyHelper.CreateOptions();

                 // Selects a caching configuration according to the current custom string
                 switch (custom)
                 {
                     case "Default":
                         // Sets the variables that compose the cache key for the 'Default' VaryByCustom string
                         options
                             .VaryByHost()
                             .VaryByBrowser()
                             .VaryByUser();
                         break;

                     case "OnlineMarketing":
                         // Sets the variables that compose the cache key for the 'OnlineMarketing' VaryByCustom string
                         options
                             .VaryByCookieLevel()
                             .VaryByPersona()
                             .VaryByABTestVariant();
                         break;
                 }

                 // Combines individual 'VaryBy' key parts into a cache key under which the output is cached
                 string cacheKey = OutputCacheKeyHelper.GetVaryByCustomString(context, custom, options);

                 // Returns the constructed cache key
                 if (!String.IsNullOrEmpty(cacheKey))
                 {
                     return cacheKey;
                 }

                 // Calls the base implementation if the provided custom string does not match any predefined configurations
                 return base.GetVaryByCustomString(context, custom);
             }


     ```

     > **Note:** **Important**
     >
     > The cache keys created in the example above ensure caching for a broad range of variables contained within each request. This may be unnecessary for your website. For optimal caching, you need to prepare output cache variables tailored according to personalization conditions that you use on the site or individual pages.
     >
     > For example, pages that check for contact personas in their personalization condition only require the persona identifier in the cache variables, and you can share the cached content for other variables, such as contact groups to which the contacts belong. On the other hand, strongly personalized pages with dynamic content may require separate cache versions for every contact.
     >
     > You always need to balance website performance, memory usage on the server, and the risk of displaying incorrect cached content.
2. Edit the controller classes that serve personalized content on your MVC site.
3. Set the **VaryByCustom** property of _OutputCache_ attributes to one of the specified configurations according to the action's caching requirements.

   ```csharp title="Example"

   using System.Web.Mvc;

   using CMS.ContactManagement;

   using LearningKit.Models.Personalization;

   namespace LearningKit.Controllers
   {
       public class PersonalizationController : Controller
       {
           /// <summary>
           /// Gets the current contact, if contact tracking is enabled for the connected Xperience instance.
           /// </summary>
           private ContactInfo CurrentContact => ContactManagementContext.GetCurrentContact();

           /// <summary>
           /// Displays a page with a personalized greeting.
           /// The content depends on whether the current contact belongs to the "YoungCustomers" persona.
           /// Caches the output for 10 minutes, with different cache versions defined by the "OnlineMarketing" custom string.
           /// The "OnlineMarketing" configuration separately caches each combination of persona and AB test variant variables.
           /// </summary>
           [OutputCache(Duration = 600, VaryByCustom = "OnlineMarketing")]
           public ActionResult PersonalizedGreeting()
           {
               CurrentContactViewModel model;

               // If on-line marketing is disabled, CurrentContact is null
               if (CurrentContact != null)
               {
                   model = new CurrentContactViewModel(CurrentContact);
               }
               else
               {
                   model = null;
               }

               return View(model);
           }
       }
   }

   ```

The application now caches separate versions of action output based on your custom variables. This ensures that visitors do not receive incorrect cached content when viewing personalized pages.

## Varying the output cache using additional variables

In addition to the cache variables provided by the system, you can vary the cache key based on any accessible request variable.

There are two ways to extend the general support provided by Xperience:

- [Implement additional extension methods for IOutputCacheKeyOptions](#implementing-additional-cache-key-parts)
- [Modify the constructed cache key by appending additional request variables](#modifying-the-constructed-cache-key)

### Implementing additional cache key parts

You can extend the provided support by implementing additional cache key parts for _IOutputCacheKeyOptions_. This approach allows for easy reusability of the implemented methods across multiple projects.

Cache key parts consist of:

- A class implementing the _IOutputCacheKey_ interface. The interface defines members necessary to implement additional cache key parts.
- An extension method for _IOutputCacheKeyOptions._ The method needs to add an instance of the _IOutputCacheKe&#x79;_&#x6F;bject to the cache key part collection of the options object by calling _IOutputCacheKeyOptions.AddCacheKey_.

The following sample demonstrates the implementation of a cache key that varies cached content based on a [contact's](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/contact-management/working-with-contacts.md) gender.

```csharp title="Implementing a contact gender cache key"

using System.Web;

using CMS.ContactManagement;

using Kentico.Web.Mvc;

namespace OutputCacheCustomization
{
    public class ContactGenderOutputCacheKey : IOutputCacheKey
    {
        // Used as a prefix for this cache key part
        public string Name => "KenticoContactGender";

        // Invoked when constructing a cache key from the configured 'IOutputCacheKeyOptions' options object
        public string GetVaryByCustomString(HttpContextBase context, string custom)
        {
            // Gets the current contact, without creating a new anonymous contact for new visitors
            ContactInfo existingContact = ContactManagementContext.GetCurrentContact(createAnonymous: false);
            // Gets the contact's gender
            int? contactGender = existingContact?.ContactGender;
            return $"{Name}={contactGender}";
        }
    }
}

```

Create a corresponding extension method for _IOutputCacheKeyOptions._ The method adds an instance of the class to an _IOutputCacheKeyOptions_ object using the _AddCacheKe&#x79;_&#x6D;ethod.

```csharp title="Implementing the extension method for IOutputCacheKeyOptions"

using Kentico.Web.Mvc;

namespace OutputCacheCustomization
{
    public static class OutputCacheKeyOptionsExtensions
    {
        // Varies the output cache based on the contact's gender
        public static IOutputCacheKeyOptions VaryByContactGender(this IOutputCacheKeyOptions options)
        {
            // Adds the ContactGenderOutputCacheKey to the options object
            options.AddCacheKey(new ContactGenderOutputCacheKey());

            return options;
        }
    }
}

```

You can now use the _ContactGender_ variable when building cache keys in the site's _GetVaryByCustomString_ implementation.

### Modifying the constructed cache key

An alternative approach to implementing [reusable extension methods](#implementing-additional-cache-key-parts) is constructing the cache key using _OutputCacheKeyHelper.GetVaryByCustomString_ and further customizing it by directly appending additional request variables.

The following example implements a custom _GetContactGroupsCacheKey_ method that returns a semicolon-separated list of all contact groups to which the current contact belongs. The string is appended to the cache key constructed by the _GetVaryByCustomString_ helper method, adding another level of variance to the output cache.

```csharp

private string GetContactGroupsCacheKey()
{
    // Gets the current contact without creating a new anonymous contact for new visitors
    var existingContact = ContactManagementContext.GetCurrentContact(createAnonymous: false);
    if (existingContact == null)
    {
        return String.Empty;
    }

    // Gets the contact groups assigned to the contact
    var groups = existingContact.ContactGroups?.Select(x => x.ContactGroupID).OrderBy(x => x).ToArray() ?? new int[] { };

    // Creates a string used to vary the resulting cache key
    var groupsKey = String.Join(";", groups);

    return $"ContactGroups={groupsKey}";
}

public override string GetVaryByCustomString(HttpContext context, string custom)
{
    // The configuration of the 'IOutputCacheKeyOptions' object is omitted
    ...

    // Combines individual 'VaryBy' components into a cache key under which the output is cached
    string cacheKey = OutputCacheKeyHelper.GetVaryByCustomString(context, custom, options);

    // Prepares an empty string object for the contact groups key part
    string contactGroupsKey = String.Empty;

    // Checks if the current request requires caching based on contact groups
    // As an example, we use contact group caching for the 'OnlineMarketing' configuration
    if (String.Equals(custom, "OnlineMarketing", StringComparison.InvariantCultureIgnoreCase))
    {
        contactGroupsKey = GetContactGroupsCacheKey();
    }

    // Appends the contact groups string to the end of the constructed cache key
    // Separates the key part using the pipe '|' character - a separator used internally 
    // for all cache key parts by the 'OutputCacheKeyHelper.GetVaryByCustomString' method
    if (!String.IsNullOrEmpty(contactGroupsKey))
    {
        cacheKey = String.Join("|", cacheKey, contactGroupsKey);
    }

    // Returns the constructed cache key        
    if (!String.IsNullOrEmpty(cacheKey))
    {
        return cacheKey;
    }

    // Calls the base implementation if the provided custom string does not match any predefined configuration
    return base.GetVaryByCustomString(context, custom);
}

```
