Announcements
Why Use SCAPI
Base URL and Request Formation
Quick Start
Server-Side Web-Tier Caching
SCAPI CDN Caching
SCAPI Specifications
B2C Commerce Release Notes
Ask the Community
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 applicationServing 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
When a shopper request that includes personalized=none reaches the SCAPI CDN layer:
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.
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.
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.
CDN entries are invalidated in sync with the web-tier page cache. There are two triggers:
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.
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 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:
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
personalized=none can be applied to any Shopper API, except the following:
Use personalized=none only for content that is intended to be identical for all shoppers.
Warning
The cf-cache-status response header reports how the CDN layer resolved the request. It reflects only the CDN tier.
| Value | Description |
|---|---|
HIT | The response was served from the CDN cache. |
MISS | The response was not in the CDN cache; it was fetched from the origin and may now be cached. |
EXPIRED | The resource was found in the CDN cache but had expired, so it was served from the origin. |
UPDATING | The resource had expired but was served from the CDN cache while the origin refreshed it in the background. Expected during stale-while-revalidate. |
DYNAMIC | The 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
Consider a shopper GET request that includes personalized=none.
First request (both tiers empty):
cf-cache-status is MISS. The request is forwarded to the origin.sfdc_cache_status is MISS [0/1]. The B2C Commerce application computes the response.Subsequent identical request (served from the edge):
The response is returned directly from the CDN, and the origin is not contacted:
1cf-cache-status: HITBecause 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
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.personalized=none and that the site has page caching enabled. A response without personalized=none is never cached at the CDN layer.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.