SCAPI CDN Caching

CDN caching is an upcoming capability that’s still rolling out. You can add personalized=none to eligible requests beginning with the B2C Commerce 26.8 release. Responses begin serving from the CDN edge after the rollout completes.

Note

SCAPI adds a Content Delivery Network (CDN) caching layer in front of the existing Server-Side Web-Tier Caching. This creates a two-tier cache for eligible, non-personalized SCAPI GET responses:

1Shopper  →  SCAPI CDN cache (edge)  →  Web-tier cache (origin)  →  B2C Commerce application

Serving eligible responses from the CDN edge answers requests closer to the shopper, reducing latency for headless and composable storefront traffic.

CDN caching applies only to responses that are explicitly marked as non-personalized. A request is eligible for CDN caching only when it includes the personalized=none query parameter. See The personalized=none Query Parameter.

Note

How It Works 

When a shopper request that includes personalized=none reaches the SCAPI CDN layer:

  1. The CDN checks its edge cache. On a cache hit, the response is returned directly from the edge, and the origin is never contacted.
  2. On a CDN cache miss, the request is forwarded to the origin, where the existing web-tier cache is consulted. On a web-tier hit, the cached response is returned; on a miss, the B2C Commerce application computes the response.
  3. The origin returns the response together with its remaining time-to-live (TTL). The CDN stores the response for that TTL, so subsequent identical requests are served from the edge.

The CDN caching layer’s mechanisms for enablement, TTL, and invalidation are kept in sync with the web-tier cache. The behavior described in Server-Side Web-Tier Caching determines what is cacheable and for how long; the CDN layer mirrors it.

Enablement 

CDN caching is gated by the same site-level setting as the web-tier cache. Page caching of dynamic content must be enabled for the site: in Business Manager, navigate to Administration > Sites > Manage Sites > Site Name - Cache and select Enable Page Caching.

In addition, an individual request is only cached at the CDN layer when it includes the personalized=none query parameter.

Time-to-Live 

The CDN uses the same TTLs as the web-tier cache. TTL depends on the API and, where applicable, the requested expansions, with the lowest TTL among the requested expansions determining how long the entry lives. For the full set of default TTLs per API and expansion, see Default Cache Expiration and Personalization Settings.

The same custom-code mechanism that adjusts the web-tier TTL also determines the CDN TTL. A TTL set in a hook with dw.system.Response#setExpires(milliseconds) applies to the CDN entry as well. See Change Cache Settings Programmatically.

The CDN may also serve a slightly stale entry while it refreshes the content in the background (stale-while-revalidate), so shoppers continue to receive fast responses during a refresh.

Invalidation 

CDN entries are invalidated in sync with the web-tier page cache. There are two triggers:

  • Manual: Invalidating the site page cache in Business Manager (Administration > Sites > Manage Sites > Site Name - Cache > Cache tab) purges the corresponding CDN entries as well.
  • Automated (replication): A data replication that clears a site’s page cache also purges the site’s CDN entries automatically, keeping both cache tiers consistent with the newly published data.

Both triggers execute the CDN purge by the pc:<siteId>_<pageCacheId> cache tag (see Cache Tags), so only the affected site’s page cache generation is invalidated.

In both cases, invalidation is not instantaneous: as with the web-tier cache, the clear is staggered over an interval (approximately 15 minutes) to avoid a sudden load spike on the origin.

Cache Tags 

Purge-by-tag drives CDN invalidation. Every cacheable SCAPI response carries a Cache-Tag response header with two tags:

1Cache-Tag: pc:<siteId>_<pageCacheId>,site:<siteId>
  • pc:<siteId>_<pageCacheId>: identifies the site’s current page cache generation (site ID plus page cache ID). When the page cache is cleared, the page cache ID changes and the entries tagged with the previous value are purged.
  • site:<siteId>: identifies the site, so all of a site’s CDN entries can be purged together.

The personalized=none Query Parameter 

The personalized=none query parameter marks a request as non-personalized and is required for a response to be cached at the CDN layer.

Only requests that include the personalized=none query parameter are eligible for CDN caching. Without this parameter, the response is not cached at the CDN layer and is always served from the origin.

Important

When personalized=none is present, personalization is disabled for that request:

  • The B2C Commerce application does not apply the Shopper Context to the request’s session and business logic.
  • The first response computed by the origin is cached and served to all subsequent shoppers of that request.

personalized=none takes precedence over custom code. If a hook marks the response as personalized with dw.system.Response#setVaryBy(identifier), that setVaryBy call is suppressed when personalized=none is honored, and the response is still treated as non-personalized and cached.

Important

Applicable APIs 

personalized=none can be applied to any Shopper API, except the following:

  • Shopper Login (SLAS)
  • Shopper Context

Use personalized=none only for content that is intended to be identical for all shoppers.

Warning

The cf-cache-status Response Header 

The cf-cache-status response header reports how the CDN layer resolved the request. It reflects only the CDN tier.

ValueDescription
HITThe response was served from the CDN cache.
MISSThe response was not in the CDN cache; it was fetched from the origin and may now be cached.
EXPIREDThe resource was found in the CDN cache but had expired, so it was served from the origin.
UPDATINGThe resource had expired but was served from the CDN cache while the origin refreshed it in the background. Expected during stale-while-revalidate.
DYNAMICThe request was not eligible for CDN caching and was served from the origin.

cf-cache-status describes only the CDN (edge) tier. The web-tier cache reports its own result separately through the sfdc_cache_status header. See Cache Status Response Header. For further reference, see the Cloudflare cache responses documentation.

Note

Example: How a Request Travels Through Both Cache Tiers 

Consider a shopper GET request that includes personalized=none.

First request (both tiers empty):

  1. The CDN has no entry, so cf-cache-status is MISS. The request is forwarded to the origin.
  2. The web-tier cache also has no entry, so sfdc_cache_status is MISS [0/1]. The B2C Commerce application computes the response.
  3. The response is stored in the web-tier cache and in the CDN edge cache.

Subsequent identical request (served from the edge):

The response is returned directly from the CDN, and the origin is not contacted:

1cf-cache-status: HIT

Because the request never reaches the origin on a CDN hit, the web tier does not process it and sfdc_cache_status is not recalculated for that request.

It is normal to observe cf-cache-status: HIT together with sfdc_cache_status: MISS on subsequent responses. When a request is computed for the first time, its response carries sfdc_cache_status: MISS (nothing was cached in the web tier yet) and is then stored in both the web-tier cache and the CDN. Because that stored copy already has sfdc_cache_status: MISS baked in, later requests resolved from the CDN return it verbatim without traveling to the web tier. This combination reflects a CDN hit on the originally computed response and is not an indicator of a problem in the web-tier cache.

Tip

Troubleshooting 

If a response is not caching as expected, inspect the caching headers and enable verbose logging:

  • cf-cache-status: Check this response header to see whether the CDN served the response (HIT), fetched it from the origin (MISS), or treated it as non-cacheable (DYNAMIC).
  • sfdc_cache_status: Check this response header to see whether the web tier already had the response cached (HIT [1/1]), missed (MISS [0/1]), or partially resolved it (PARTIAL [N/M]). This helps distinguish a CDN miss that was still served quickly from the web-tier cache versus a full recomputation at the application. See Cache Status Response Header.
  • sfdc_verbose: true: Add this request header to collect detailed, per-request diagnostics in the logs. See Collect Detailed Request Information.
  • Eligibility: Confirm the request includes personalized=none and that the site has page caching enabled. A response without personalized=none is never cached at the CDN layer.

Custom APIs 

Custom APIs participate in CDN caching on the same basis as system Shopper APIs: a non-personalized, cacheable response is eligible for the CDN layer. The specifics of enabling caching in a Custom API implementation script—setting the cache time and marking responses as personalized—are described in Custom API Caching.