Configure cache

Overview

NOTE: Some of the settings and features described in this article are available starting with Sitefinity CMS 15.4.8624 (Product Update). If you are on an older version, they might not be available or might not work as described and it is recommended to refer to the article for Sitefinity CMS 15.3.

ASP.NET Core Renderer pages can be protected by two independent caching layers:

  1. Renderer output cache — the renderer caches rendered HTML server-side. Subsequent requests are served directly from the renderer without calling Sitefinity.
  2. CDN cache — a CDN edge caches the HTTP response from the renderer. Requests served from the CDN do not reach the renderer at all.

Both layers work together and are configured independently. Start with the renderer output cache. Add CDN caching separately if your deployment includes a CDN edge. For a high-level view of all components, see Architecture diagram at the end of this page.

NOTE: CDN caching is disabled by default — the renderer always returns Cache-Control: no-cache. You must explicitly enable it via the CdnCacheControl setting.

Renderer output cache

Absolute expiration

This is the default configuration. The Renderer is set to cache everything with 60 seconds absolute expiration. No additional configuration is required.

To change the TTL or disable caching, set OutputCacheDuration in the renderer's appsettings.json:

JSON
{
  "Sitefinity": {
    "OutputCacheDuration": 3600
  }
}

Set OutputCacheDuration to 0 to disable output caching entirely.
For more information, see Application settings.

Distributed cache (multi-server deployments)

The default 60-second TTL works for NLB scenarios — all nodes serve updated content within one minute. For longer cache durations across multiple renderer nodes, use a shared distributed cache so all nodes remain in sync at all times.

The renderer has built-in support for Redis as a distributed output cache backend. Add the RedisOutputCache connection string to the renderer's appsettings.json:

JSON
{
  "ConnectionStrings": {
    "RedisOutputCache": "your-redis-host:6380,ssl=true,password=..."
  }
}

When RedisOutputCache is present, the renderer automatically uses Redis as the output cache store across all nodes.

If Redis does not fit your infrastructure, you can implement a custom distributed cache backend using the IOutputCacheStore interface.

GITHUB EXAMPLE: The Output cache sample demonstrates a custom distributed cache implementation using EntityFramework and the IOutputCacheStore interface.
For more information, see Microsoft documentation » Cache storage.

Cache invalidation

For longer cache durations, the renderer must receive invalidation notifications from Sitefinity when content changes so it can purge stale entries automatically.

NOTE: In case your Sitefinity CMS is hosted under HTTP, disable the Require Https for all requests setting. Navigate to Administration » Settings » Advanced » System » Output Cache Settings » Output cache web service.

Three invalidation methods are available:

MethodBest forGuarantees CDN orderingComplexity
WebhookSimple setups; no direct HTTP access from CMS to rendererNoLow
Direct HTTP purgeAny deployment where the CMS can reach the rendererYesLow
Shared RedisCMS and renderer share the same Redis clusterYesMedium

IMPORTANT: Prefer Method B or C over Method A. With webhook, renderer and CDN webhooks fire concurrently — the CDN may re-cache stale content from the renderer before the renderer has processed its own webhook.

Step 1: Configure the shared authentication key

All three methods use the same shared secret. Configure it on both CMS and renderer before choosing a method.

Sitefinity CMS (web.config):

XML
<appSettings>
  <add key="sf-env:outputCacheConfig:optionalHeaders" value="Key" />
  <add key="sf-env:systemConfig/outputCacheSettings/cacheService:authenticationKey" value="YOUR-SHARED-SECRET-HERE" />
</appSettings>

Renderer (appsettings.json):

JSON
{
  "Sitefinity": {
    "OutputCacheDuration": 86400,
    "OutputCacheAuthKey": "YOUR-SHARED-SECRET-HERE"
  }
}

Set OutputCacheDuration to a longer period — for example, 86400 (24 hours). Content stays fresh because Sitefinity triggers invalidation when pages or content items change.

NOTE: When not all CMS API responses for a page include sf-cache-key headers, the renderer caches that page at OutputCacheMissingKeysDuration seconds (default: 60) instead of OutputCacheDuration, without full invalidation coverage. This typically occurs for pages that include widgets which do not register output cache dependencies. Set OutputCacheMissingKeysDuration to 0 to skip caching for such pages entirely.

Method A: Webhook

The CMS raises IOutputCacheInvalidationEvent and a service hook sends an HTTP POST to the renderer's purge endpoint.

Pros: Simple; works in any network topology.
Cons: CDN ordering is not guaranteed — renderer and CDN webhooks fire concurrently. If you have both renderer and CDN cache, switch to Method B or C.

Setup:

Create a service hook using procedure Create service hooks with the following properties:

  1. In Trigger, select Custom event.

  2. In Custom event type, enter
    Telerik.Sitefinity.Web.OutputCache.IOutputCacheInvalidationEvent

  3. In Action, keep Send data to URL.

  4. In Target URL, enter https://rendererdomain/sfrenderer/api/v1/cache/purge

    NOTE: The Service hooks UI does not allow localhost domains. Update the URL via the file system after saving the service hook.

  5. In HTTP Headers, add a header with Key equal to SF_OUTPUTCACHE_AUTH and Value equal to YOUR-SHARED-SECRET-HERE.
    The value must match OutputCacheAuthKey in the renderer's appsettings.json.

RESULT: Sitefinity notifies the renderer whenever a page is invalidated, so the renderer can purge that page from its cache.

Troubleshooting:

Renderer is not receiving purge notifications:

  • Verify the service hook is registered in Administration » Settings » Advanced » System » Service Hooks.
  • Verify the event type is exactly Telerik.Sitefinity.Web.OutputCache.IOutputCacheInvalidationEvent.
  • Verify the renderer's purge endpoint is reachable from the CMS server.
  • Confirm the SF_OUTPUTCACHE_AUTH header value matches OutputCacheAuthKey on the renderer.
  • Check CMS error logs for webhook dispatch failures.

CDN serves stale content even though the renderer webhook fired:

  • Renderer and CDN webhooks fire concurrently — this is an inherent limitation of Method A. Switch to Method B or C.

Method B: Direct HTTP purge

The CMS calls the renderer's purge endpoint directly before raising IOutputCacheInvalidationEvent. CDN webhooks fire only after the renderer has been purged.

Pros: CDN ordering guaranteed; no changes needed on the renderer side.
Cons: Requires direct HTTP access from the CMS server to the renderer host.

Setup:

In web.config, add the renderer base URL:

XML
<appSettings>
  <add key="sf-env:systemConfig/outputCacheSettings/cacheService:rendererUrl"
       value="https://your-renderer-host.example.com" />
  <!-- Optional: timeout in seconds for the purge call (default: 30) -->
  <add key="sf-env:systemConfig/outputCacheSettings/cacheService:rendererCachePurgeTimeoutInSeconds"
       value="30" />
</appSettings>

The authenticationKey from Step 1 is reused for the SF_OUTPUTCACHE_AUTH header — no additional secret is needed. Once direct purge is verified working, you can remove any webhook service hook previously registered for the renderer.

Troubleshooting:

Renderer is not being purged:

  • Verify rendererUrl is set and points to the correct base URL of the renderer.
  • Confirm the renderer's purge endpoint is reachable from the CMS server.
  • Check CMS error logs for HTTP errors on the purge call.
  • Verify authenticationKey on the CMS matches OutputCacheAuthKey on the renderer.

Purge calls are timing out:

  • Increase rendererCachePurgeTimeoutInSeconds (default: 30 seconds). This may be needed if the renderer is under load at invalidation time.

Method C: Shared Redis

The CMS deletes renderer cache entries directly from a shared Redis instance — no HTTP round-trip required.

Pros: Lowest latency; CDN ordering guaranteed; no HTTP calls during invalidation.
Cons: Requires CMS and renderer to share the same Redis instance; higher initial setup complexity.

Prerequisites:

  • CMS output cache provider set to Redis.
  • CMS and renderer configured to use the same Redis connection.

Setup:

Renderer (appsettings.json):

JSON
{
  "ConnectionStrings": {
    "RedisOutputCache": "your-shared-redis:6380,ssl=true,password=..."
  },
  "Sitefinity": {
    "EnableSharedDistributedCache": true,
    "SharedCacheKeyPrefix": "sf-oc-"
  }
}

Sitefinity CMS (web.config):

XML
<appSettings>
  <add key="sf-env:systemConfig/outputCacheSettings/pageCacheInvalidation:sharedRendererCacheEnabled"
       value="true" />
  <add key="sf-env:systemConfig/outputCacheSettings/pageCacheInvalidation:sharedRendererCacheKeyPrefix"
       value="sf-oc-" />
</appSettings>

IMPORTANT: sharedRendererCacheKeyPrefix on the CMS and SharedCacheKeyPrefix on the renderer must be identical. A mismatch prevents the CMS from locating and deleting renderer cache entries.

Troubleshooting:

Renderer cache entries are not being deleted:

  • Verify EnableSharedDistributedCache is true in the renderer's appsettings.json.
  • Verify the key prefix values match exactly between CMS and renderer.
  • Confirm both the renderer's RedisOutputCache connection string and the CMS Redis connection string point to the same Redis instance.
  • Verify the CMS output cache provider is set to Redis — shared invalidation does not work with InMemory or other providers.
  • Check CMS error logs for Redis connection errors.

CDN caching

When the renderer is hosted behind a CDN, you can configure the renderer to forward the CMS page-level Cache-Control header to the CDN. The renderer output cache and CDN cache operate independently — both can be active simultaneously on the same request.

PREREQUISITES: OutputCacheAuthKey must be set on the renderer before enabling CDN caching. The CMS must return sf-cache-key and Cache-Control: public headers in page model responses.

Enable CDN caching

CDN caching is controlled by the CdnCacheControl setting in the renderer's appsettings.json:

ValueBehaviour
Off (default)Renderer always returns Cache-Control: no-cache. CDN does not cache.
DefaultVariationOnlyForwards the CMS Cache-Control header for anonymous requests only. Authenticated requests receive no-cache. Use when the CDN does not vary its cache by user roles.
AllVariationsForwards the CMS Cache-Control header for all requests, including role-based cache variations. Use when the CDN is configured to vary its cache by the Sitefinity roles cookie.

NOTE: When CdnCacheControl is DefaultVariationOnly, only anonymous requests receive the forwarded Cache-Control header. Authenticated requests always receive Cache-Control: no-cache and are not cached at the CDN regardless of the page cache profile. Use AllVariations to forward Cache-Control for authenticated requests too — this requires the CDN to vary its cache by the Sitefinity roles cookie.

Configuration example for anonymous CDN caching:

JSON
{
  "Sitefinity": {
    "OutputCacheAuthKey": "YOUR-SHARED-SECRET",
    "CdnCacheControl": "DefaultVariationOnly"
  }
}

The CDN cache TTL is taken from the CMS page cache profile. Configure it by navigating to Administration » Settings » Advanced » System » Output Cache Settings » Page Cache profiles » Standard Caching.

CDN cache invalidation

When content changes in Sitefinity, the CDN must purge its cached entries. Configure a service hook to call your CDN purge endpoint.

  1. Create a service hook using procedure Create service hooks with the following properties:
    1. In Trigger, select Custom event.
    2. In Custom event type, enter
      Telerik.Sitefinity.Web.OutputCache.IOutputCacheInvalidationEvent
    3. In Action, keep Send data to URL.
    4. In Target URL, enter your CDN purge endpoint URL.
    5. Optionally, configure authentication headers required by your CDN provider.
  2. Click Create this hook.

The payload sent to the purge endpoint contains each expired URL:

JSON
{
    "Name": "",
    "OriginalEvent": {
        "ActivityId": "",
        "ExpiredItems": [
            {
                "Key": "",
                "Url": "",
                "Host": "",
                "SiteId": "",
                "ETag": "",
                "Flags": ""
            }
        ]
    }
}

NOTE: When both renderer cache invalidation and CDN cache invalidation are active, use Method B (direct HTTP purge) or Method C (shared Redis) for the renderer. This guarantees the renderer is purged before the CDN webhook fires. With Method A, both webhooks fire concurrently and the CDN may re-cache stale content from the renderer.

Troubleshooting — CDN caching

CDN is not caching pages:

  • Verify CdnCacheControl is not set to Off.
  • Verify OutputCacheAuthKey is set on the renderer — this is required for cache-control header forwarding.
  • Verify the CMS page cache profile has a positive Duration value.
  • If CdnCacheControl is DefaultVariationOnly, confirm the request is anonymous — authenticated requests always receive no-cache.

CDN serves stale content after a publish:

  • Verify the service hook is registered and the CDN purge endpoint URL is reachable from the CMS server.
  • Check CMS error logs for webhook dispatch failures.
  • For Sitefinity Cloud: verify the CDN Purger Azure Function is running (check Application Insights) and the cdn-purge-requests Azure Queue is not backed up.

Advanced configuration

Role-based cache variation

When your site has pages whose content differs by the visitor's roles, configure both the CMS and the renderer to maintain separate cache versions per role group.

IMPORTANT: Role-based cache variation is not suitable when permissions are assigned per individual user or when Deny permission entries are used. The variation mechanism groups visitors by role set only and cannot produce a unique cache bucket per user. For pages with per-user permissions, disable output caching for those pages by calling PageRouteHandler.RegisterNoCacheAuthenticatedUserOutputCacheVariation().

Recommended configuration:

Sitefinity CMS (web.config):

XML
<appSettings>
  <!-- Cache CMS content responses by role set -->
  <add key="sf-env:systemConfig/outputCacheSettings:contentResponseCachingBehavior" value="VaryByUserRoles" />
  <!-- Prevent per-user View permissions that would break the role-based cache model -->
  <add key="sf-env:systemConfig/outputCacheSettings:enforceRoleOnlyViewPermissions" value="true" />
  <!-- Issue a signed roles cookie on sign-in -->
  <add key="sf-env:authenticationConfig:enableCdnRolesCookie" value="true" />
  <add key="sf-env:authenticationConfig:cdnRolesCookieEncryptionKey" value="YOUR-HEX-KEY-HERE" />
  <add key="sf-env:authenticationConfig:cdnRolesCookieName" value="sf-roles" />
  <add key="sf-env:authenticationConfig:cdnRolesCookieTtlMinutes" value="60" />
</appSettings>

Renderer (appsettings.json):

JSON
{
  "Sitefinity": {
    "EnableOutputCacheVaryByRolesCookie": true,
    "OutputCacheRolesCookieSigningKey": "YOUR-HEX-KEY-HERE",
    "OutputCacheVaryByRolesCookieName": "sf-roles"
  }
}

OutputCacheRolesCookieSigningKey must be the same hex value as cdnRolesCookieEncryptionKey on the CMS.

Troubleshooting:

Roles cookie is not issued on sign-in:

  • Verify enableCdnRolesCookie is true and cdnRolesCookieEncryptionKey is at least 32 hex characters. A missing or too-short key causes a warning in the CMS error log and suppresses cookie creation.
  • Verify the user has at least one role assigned — the cookie is not issued for users with no roles or for backend users.

Authenticated users still get no-cache responses even when VaryByUserRoles is configured:

  • Check whether a widget on the page calls PageRouteHandler.RegisterNoCacheAuthenticatedUserOutputCacheVariation() (for example, UserProfileView). That call overrides contentResponseCachingBehavior for the entire page.

Role-based variation produces incorrect content for some users:

  • Verify that your permission model does not use per-user View permissions or Deny entries. Enable enforceRoleOnlyViewPermissions to prevent this misconfiguration at the source.

Protect against query-string URL explosion

Every unique URL cached by Sitefinity creates a database record. Bots that request the same page with many different query strings can cause uncontrolled database growth and degrade cache lookup performance.

Enable probation to protect against this. On the first visit to an unseen query-string URL, Sitefinity caches the response briefly without writing a database record. The URL is promoted to full DB-backed caching only if it is seen again within the probation window.

XML
<appSettings>
  <!-- Probation window (minutes). Set to 0 to disable. Default: 0 (disabled). -->
  <add key="sf-env:systemConfig/outputCacheSettings/pageCacheInvalidation:probationWindowInMinutes"
       value="5" />
  <!-- Response TTL during the first visit (seconds). Default: 0 (no cache). -->
  <add key="sf-env:systemConfig/outputCacheSettings/pageCacheInvalidation:probationCacheDurationInSeconds"
       value="60" />
  <!-- Skip DB write when remaining TTL is at or below this value (seconds). Default: 0. -->
  <add key="sf-env:systemConfig/outputCacheSettings/pageCacheInvalidation:persistDependenciesMinDurationInSeconds"
       value="60" />
</appSettings>

A probationWindowInMinutes value of 5 is recommended for production. Probation is disabled by default.

Renderer settings reference

All output cache settings are configured under the "Sitefinity" section of the renderer's appsettings.json:

SettingTypeDefaultDescription
OutputCacheDurationint (seconds)60Server-side cache TTL. Set to 0 to disable output caching.
OutputCacheAuthKeystringShared secret for dependency registration and the purge endpoint. Required for cache invalidation and CDN caching.
CdnCacheControlstringOffCDN Cache-Control forwarding mode: Off, DefaultVariationOnly, or AllVariations.
OutputCacheVaryByCookiesstringComma-separated cookie names to include in the cache key.
ExternalCacheRequestSyncTimeoutint (seconds)30Timeout for dependency registration requests sent from the renderer to the CMS.
OutputCacheMissingKeysDurationint (seconds)60Fallback TTL when not all CMS responses include sf-cache-key. Set to 0 to return no-cache when keys are missing.
EnableSharedDistributedCacheboolfalseEnables shared Redis invalidation (Method C).
SharedCacheKeyPrefixstringsf-oc-Key prefix for shared Redis tag mappings. Must match sharedRendererCacheKeyPrefix on the CMS.
EnableOutputCacheVaryByRolesCookieboolfalseVary the renderer cache by the CMS-issued signed CDN roles cookie.
OutputCacheRolesCookieSigningKeystringHex HMAC-SHA256 key for validating the roles cookie. Required when EnableOutputCacheVaryByRolesCookie is true. Must match cdnRolesCookieEncryptionKey on the CMS.
OutputCacheVaryByRolesCookieNamestringsf-rolesName of the signed CDN roles cookie issued by the CMS.
EnableOutputCacheStatusHeaderboolfalseWhen true, adds sf-cache-status: HIT or MISS to responses. Useful for diagnostics; disable in production.

Override any setting per environment using ASP.NET Core environment variables with __ as the separator:

Sitefinity__OutputCacheDuration=86400
Sitefinity__OutputCacheAuthKey=YOUR-SECRET-HERE
Sitefinity__CdnCacheControl=DefaultVariationOnly
Sitefinity__EnableSharedDistributedCache=true

Architecture diagram

The following diagram shows the full output cache architecture, including the renderer output cache, CDN caching, and all three renderer cache invalidation methods:

Output cache architecture

Want to learn more?
Enhance your Sitefinity skills by enrolling in free training sessions. Become Sitefinity certified through Progress Education Community to strengthen your professional credentials.