---
title: Caching in the Salesforce Composable Storefront
canonical_url: 'https://rhino-inquisitor.com/caching-in-the-sfcc-composable-storefront/'
markdown_url: >-
  https://rhino-inquisitor.com/caching-in-the-sfcc-composable-storefront/index.md
content_type: article
site_name: Rhino Inquisitor
date: '2024-10-14T09:42:12Z'
lastmod: '2024-10-14T12:49:23Z'
description: >-
  Learn how caching works in the Salesforce Composable Storefront, which layers
  matter most, and where it improves real-world speed.
author: Thomas Theunen
categories:
  - Salesforce Commerce Cloud
  - Technical
tags:
  - composable storefront
  - sfcc
  - technical
---
## Key Takeaways
- Explains where SSR, CDN, and API caching fit in the Composable Storefront stack
- Highlights cache-control, invalidation, and replication caveats teams need to plan for
- Covers personalized caching behavior for custom APIs and hook-based responses

Caching, Performance, Lighthouse Speed, CrUX, … and probably many more terms have crossed your desk and mind ever since you got into web development and, more specifically, if you are here, Salesforce B2C Commerce Cloud.

With the platform’s various iterations, understanding the differences is crucial. [SFRA](/sitegenesis-vs-sfra-vs-pwa/) differs from the Composable Storefront, so your experience with one may not directly apply to the other.

But you aren’t here for pretty intro speeches and chit-chat—let’s get into the options you should know!

## Server-Side Rendering & Caching

When we talk about the server side in the Composable Storefront, we shouldn’t forget that our “head” has now been completely separated from the “body”, which is an entirely different way of thinking compared to SFRA.

And within that “Head”, the PWA-Kit, we have the option to make use of Server Side Rendering.

But why do we even have this concept?

### Why Server Side Rendering

For several reasons, server-side Rendering (SSR) is essential in a Single Page Application (SPA), especially for an e-commerce website.

SSR helps improve the website’s search engine optimisation (SEO). Search engines rely on the initial HTML content of a page to index its content. With SSR, the server sends fully rendered HTML to the client, making it easier for search engine crawlers to index the site, resulting in better visibility and ranking in search results.

It also significantly boosts the initial page load time and overall website performance. By rendering the initial HTML content on the server and sending it to the client, SSR reduces the time it takes for the user to see and interact with the content. This leads to a better user experience, a critical factor for e-commerce sites where user engagement directly impacts sales.

SSR also plays a significant role in social media sharing and link previews. When a link to a page is shared on social media platforms, having pre-rendered HTML content allows for a better link presentation, including rich previews with images and metadata. This can significantly improve click-through rates and user engagement.

### But it should be cached

Just like SFRA and SiteGenesis, caching the HTML generated by the server will significantly improve the performance that users experience in the browser and reduce the system load. This allows the system to focus on more critical aspects of the site that cannot be cached, such as the checkout.

If you don’t cache these pages, the initial page loads will have to wait for all of the “anonymous” API calls and for building the page:

-   SLAS Session initialisation
-   Categories call for the menu
-   Your main page API (Page Designer, Product Search, …)
-   React component rendering

If they are not cached, all of these items can raise your TTFB (time to first byte) metric well above 1.5 seconds for every page visit (first-page load; after that, the client side takes over).

Luckily, we can easily enable this mechanic via the code:

```js
const Home = () => {
    ...
    const {res} = useServerContext()
    if (res) {
        res.set(
            'Cache-Control',
            `s-maxage=${MAX_CACHE_AGE}, stale-while-revalidate=${STALE_WHILE_REVALIDATE}`
        )
    }
    ...
}
```

> **Cdn:** Know that the CDN part of the Managed Runtime takes care of this caching mechanism. At the time of writing, this is CloudFront. The plan is to migrate to the eCDN for a generic architecture (Forward-Looking Statement).

> **Default Cache Times:** Be aware that the default cache time is only 15 minutes, which is low. Consider changing this to a few hours!

But keep in mind that the replication does not automatically clear its cache in the Managed Runtime like we are used to in SFRA, but an API is available (hint, hint).

## API Caching

When separating the head from the body, we still need a way for them to communicate. This is where various REST APIs, including SCAPI, come into play.

These APIs also have a set of rules, including caching. This is just the start when we are heading into Composable territory; each piece of the puzzle has its own set of rules!.

Some important caching caveats to be aware of:

> **Not Found:** 404 responses for a GET call are automatically cached when the feature flag “Web Adapter Caching of 404 Responses for Dynamic Content” is enabled.

> **Replication:** When replicating new code changes to the SCAPI or OCAPI, it’s important to remember that there is a 15-minute delay in cache clearing. This is because SCAPI and OCAPI caching are linked to the “Page Cache” setting that we are familiar with in SFRA and SiteGenesis.

> **Clearing The Cache:** Currently, the only way to manually invalidate the cache is to invalidate the entire site page cache.

In Business Manager, go to Administration > Sites > Manage Sites > Site Name - Cache, and then select the Cache tab. In the “Cache Invalidation” section, there is a button to invalidate the site page cache. Once you trigger the invalidation, the entire site page cache, including the cache related to SCAPI responses, will be cleared within 15 minutes.

### Standard API

Salesforce B2C Commerce Cloud comes with many REST APIs out of the box (some you can extend, others you can not). These APIs have their own rules for caching and personalised caching.

These rules have been documented [in the server-side web-tier caching guide](https://developer.salesforce.com/docs/commerce/commerce-api/guide/server-side-web-tier-caching.html); be sure to have a look!

#### Feature Switch

For most, this option will already be enabled or will not even be visible anymore in the business manager. But verify that Server-Side Web-Tier Caching has been enabled in your feature switches. (_Administration> Global Preferences > Feature Switches_)

![Business Manager feature toggle for enabling SCAPI server-side web-tier caching.](/caching-in-the-sfcc-composable-storefront/scapi-server-side-web-tier-caching-ead7ec1b79_hu_166435b552f1ff40.webp)

Figure 1: Business Manager feature toggle for enabling SCAPI server-side web-tier caching

The feature toggle in the Business Manager

### Custom & Customising APIs

#### Adjusting caching

Within a hook or custom endpoint you can modify the cache time with a simple line of code: `response.setExpires()`

```js
exports.modifyGETResponse = function(scriptCategory, categoryWO)
{
    response.setExpires(Date.now() + 3600000);
    return new Status( Status.OK );
}
```

### Personalisation

When personalisation is enabled for a resource, additional information such as active promotions, product sorting rules, price books, and ABTest groups becomes part of the cache key (besides the URL). This allows the cache to store different response variations and deliver the correct version to the API user based on this additional information.

It’s important to use the [Shopper Context API](https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-context) to apply changes in personalization-related resources and to append a custom query parameter to the URL (the primary unique key) for requests that are supposed to produce different responses due to conditional hook logic.

You can also enable this within a hook or custom API with this line of code: `response.setVaryBy("price_promotion");`

```js
exports.modifyGETResponse = function(scriptCategory, categoryWO)
{
    response.setVaryBy("price_promotion");
    return new Status( Status.OK );
}
```
