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.