Vue Storefront is now Alokai! Learn More
Setting for how long the content should be cached (TTL)

Setting for how long the content should be cached (TTL)

Caching can be thought of as storing a copy of the content and serving it to the user without having to regenerate it. TTL (time-to-live) is a setting that determines how long the content should be stored before it is considered outdated.

When a user requests a page, the CDN checks if it has an up-to-date cached version of the page. If it does, it serves the cached version to the user. If it doesn’t, it forwards the request to the origin server, caches the response, and serves it to the user.

We can set the TTL for a page by setting the Cache-Control header in the response.

The recommended default setting for the Alokai Storefront is public, max-age=0, s-maxage=15, must-revalidate. This setting disables browser cache but enables CDN TTL for 15 seconds.

It is not recommended to set long TTLs (Time-to-Live) for HTML. This is because HTML dictates what version of the frontend is loaded. If you deploy a new version and the user receives an old HTML, it will make requests to the middleware in an outdated format, which the new version of the middleware may not understand.

Just like the CDN stores the cache between the user's machine and your server, the browser cache stores the cache on the user's machine. This has some consequences. The main ones are that the browser cache is very fast, but it is not shared between users, and you have no easy way to recover from mistakes in setting it. If a page is incorrectly cached in the browser cache, there is no way for you to invalidate the cache on the user’s machine; you have to wait for the TTL to expire.

Modifying .env

To inform the application about the default cache control value, we need to add it to the .env file.

Modify both apps/storefront-unified-nextjs/.env.example and apps/storefront-unified-nextjs/.env as follows:

...
+NEXT_DEFAULT_HTML_CACHE_CONTROL="public, max-age=0, s-maxage=15, must-revalidate"

Creating a TTL Helper

Create a helper that allows for setting TTL on a page. This helper will allow us to easily set the cache control header per page.

import type { IncomingMessage, ServerResponse } from 'http';

export function setCacheControl(res: ServerResponse<IncomingMessage>, custom?: string) {
  // WARNING: it doesn't work in dev mode, since Next disables caching
  // while running the app in this mode.
  const cacheControlSettings = custom ?? process.env.NEXT_DEFAULT_HTML_CACHE_CONTROL;
  if (!cacheControlSettings) return;
  res.setHeader('Cache-Control', cacheControlSettings);
  res.setHeader('Vary', 'Accept-Encoding');
}

This helper function checks for custom cache control settings or uses a default value from environment variables. It then sets the appropriate headers on the response object.

Usage

To invoke the TTL Helper within the createGetServerSideProps section of a page, follow these steps:

1

Import the createGetServerSideProps and setCacheControl helpers.

2

Use the setCacheControl function to set the cache control headers within the getServerSideProps function. Here is how you can do it:

+import { createGetServerSideProps, setCacheControl } from '~/helpers/ssr'; // or wherever the helper is located 
// ...
export const getServerSideProps = createGetServerSideProps({ i18nNamespaces: ['product'] }, async (context) => {
+  const { res } = context;
+  setCacheControl(res); 
  // ...

Deployment

To add the new default cache control value to the Dockerfile, you need to set an environment variable in the Dockerfile that the application can read.

...
+ENV NEXT_DEFAULT_HTML_CACHE_CONTROL=${NEXT_DEFAULT_HTML_CACHE_CONTROL} 

FROM base AS builder

And set its value in the CI/CD pipeline:

  build-frontend:
    name: Build Frontend
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Build
        uses: vuestorefront/storefront-deployment/build-frontend@v3.2.0
        with:
          # Change frontend to desired one
          frontend: ${{ vars.FRONTEND_FRAMEWORK || secrets.FRONTEND_FRAMEWORK || 'next' }}
          docker_registry_url: ${{ vars.DOCKER_REGISTRY_URL || secrets.DOCKER_REGISTRY_URL }}
          project_name: ${{ vars.PROJECT_NAME || secrets.PROJECT_NAME }}
          cloud_username: ${{ vars.CLOUD_USERNAME || secrets.CLOUD_USERNAME }}
          cloud_password: ${{ secrets.CLOUD_PASSWORD }}
          cloud_region: ${{ vars.CLOUD_REGION || secrets.CLOUD_REGION }}
          npm_email: ${{ vars.NPM_EMAIL || secrets.NPM_EMAIL }}
          npm_user: ${{ vars.NPM_USER || secrets.NPM_USER }}
          npm_pass: ${{ secrets.NPM_PASS }}
          api_base_url: ${{ vars.API_BASE_URL || format('https://{0}.{1}.{2}/api', vars.PROJECT_NAME || secrets.PROJECT_NAME, vars.CLOUD_REGION || secrets.CLOUD_REGION, 'gcp.storefrontcloud.io') }}
+         default_html_cache_control: ${{ vars.DEFAULT_HTML_CACHE_CONTROL || 'public, max-age=0, s-maxage=15, must-revalidate' }}

Validating if we did everything correctly

1

Run the project. If you are using Next.js, then you have to run it in a prod mode. So yarn build && yarn start from within the frontend’s directory apps/storefront-unified-nextjs. The Middleware can be executed in dev mode so yarn dev from within apps/storefront-middleware will work.

2

Open the network tab of your browser’s dev tools

3

The first request upon visiting the page - the one that downloads initial HTML - should receive Cache-Control header attached to the response.

Network Tab showing Cache-Control header attached to the response