Purge by keys
The documentation only applies to instances deployed on Alokai@Edge.
Purge by keys lets you selectively remove cached content from the CDN without purging your entire cache. Instead of invalidating individual URLs or all content at once, you tag responses with surrogate keys and then purge all objects sharing a given key in a single operation.
This is especially useful when a content change (for example, a product update, a price change, or a new translation) affects multiple cached pages at once.
How surrogate keys work
A surrogate key is a label attached to a cached response. When your origin serves a response, it can include one or more surrogate keys via the Surrogate-Key HTTP header. The CDN stores the mapping between each key and the cached object. Later, when you issue a purge request for a specific key, every cached object tagged with that key is removed.
A single response can carry multiple keys, and the same key can appear across many different responses. This creates a flexible many-to-many relationship between keys and cached objects.
Example: Your origin responds with:
Surrogate-Key: product-123 category-shoes homepage
This means the cached response is tagged with three keys. Purging product-123 removes this object (and any other object also tagged with product-123) from the cache, while leaving unrelated content untouched.
For a deeper technical explanation of surrogate keys and the Surrogate-Key header, see the Fastly documentation on working with surrogate keys.
Limitations:
- Individual keys must not exceed 1,024 bytes.
- The total
Surrogate-Keyheader value must not exceed 16,384 bytes. - Duplicate keys in the same header are collapsed into a single entry.
- Keys are space-separated — a key itself cannot contain spaces.
Automatic host key
Alokai automatically adds the request's browser domain (the Host header value) as a surrogate key to every cached response. This means you can purge all cached content for a specific domain without affecting other domains served by the same instance.
For example, if your instance serves both store.example.com and store.example.de, you can purge all cached content for the German storefront by entering store.example.de as a key — without touching the cache for store.example.com.
Format: Use the bare domain as it appears in the browser address bar — no protocol, no trailing slash, no path.
| Correct | Incorrect |
|---|---|
store.example.com | https://store.example.com |
store.example.de | store.example.com/ |
Configure your application
For the CDN to tag cached responses with surrogate keys, your application must include the Surrogate-Key HTTP header in its SSR responses. The CDN reads this header, stores the key-to-object mapping, and then strips the header before sending the response to the end user — your keys are never exposed to browsers.
The header value is a space-separated list of keys:
Surrogate-Key: product-123 category-shoes homepage
This approach mirrors how you already set Cache-Control headers for TTL (see Enabling SSR Caching). You create a helper that collects keys during rendering and writes them into the response header.
Key naming strategies
How you structure your keys is up to you. Common strategies include tagging by:
- Entity ID —
product-123,category-42,cms-page-about - Content type —
pdp,plp,cms - Locale or market —
en-us,de-de - Combination —
product-123 plp en-us
Plan your naming convention early — a consistent taxonomy like {entity-type}-{id} makes purging predictable and easier to automate.
Next.js
In the Next.js App Router, response headers are set through the middleware layer. Create a helper that builds the Surrogate-Key header based on the current route.
1. Create a surrogateKeys helper
// apps/storefront-unified-nextjs/surrogate-keys.ts
import { type NextRequest, NextResponse } from 'next/server';
import { match } from 'path-to-regexp';
type KeyResolver = (params: Record<string, string>) => string[];
interface SurrogateKeysOptions {
matchers: Record<string, KeyResolver>;
defaultKeys?: string[];
}
function surrogateKeys({ matchers, defaultKeys = [] }: SurrogateKeysOptions) {
return (request: NextRequest, response: NextResponse) => {
const keys = new Set<string>(defaultKeys);
for (const [pattern, resolver] of Object.entries(matchers)) {
const urlMatch = match(pattern, { decode: decodeURIComponent });
const result = urlMatch(request.nextUrl.pathname);
if (result) {
for (const key of resolver(result.params as Record<string, string>)) {
keys.add(key);
}
}
}
if (keys.size > 0) {
response.headers.set('Surrogate-Key', [...keys].join(' '));
}
return response;
};
}
export { surrogateKeys };
2. Use the helper in your middleware
Wire the surrogateKeys helper into createAlokaiMiddleware, alongside your existing cacheControl setup:
// apps/storefront-unified-nextjs/proxy.ts
import { createAlokaiMiddleware } from '@vue-storefront/next';
import { surrogateKeys } from './surrogate-keys';
const surrogateKeysMiddleware = surrogateKeys({
defaultKeys: ['storefront'],
matchers: {
'/product/:slug/:id': (params) => [
`product-${params.slug}`,
'pdp',
],
'/category/*slugs': () => [
'plp',
],
'/': () => ['homepage'],
'/search': () => ['search'],
},
});
export default createAlokaiMiddleware(async (request) => {
// ... your existing middleware logic ...
response = surrogateKeysMiddleware(request, response);
return response;
});
Note: The patterns above match URLs for the default locale, which has no locale prefix (e.g.,
/product/my-slug/123). Non-default locales include a prefix (e.g.,/de/product/my-slug/123). Add prefixed variants as needed, for example'/de/product/:slug/:id'.
Nuxt
In Nuxt, you can set response headers using a composable that wraps the server event.
1. Create a useSurrogateKeys composable
// apps/storefront-unified-nuxt/composables/useSurrogateKeys/useSurrogateKeys.ts
export function useSurrogateKeys() {
const event = useRequestEvent();
if (!event) return { addKeys: () => {} };
const existing = event.node.res.getHeader('Surrogate-Key') as string | undefined;
const keys = new Set<string>(existing ? existing.split(' ') : []);
function addKeys(...newKeys: string[]) {
for (const key of newKeys) {
keys.add(key);
}
event.node.res.setHeader('Surrogate-Key', [...keys].join(' '));
}
return { addKeys };
}
2. Use the composable in pages
Call addKeys at the top of any page or layout to tag the response with the relevant surrogate keys:
<!-- apps/storefront-unified-nuxt/pages/product/[slug]/[productId].vue -->
<script setup lang="ts">
const route = useRoute();
const { addKeys } = useSurrogateKeys();
addKeys(`product-${route.params.slug}`, 'pdp');
// ... rest of your page logic ...
</script>
<!-- apps/storefront-unified-nuxt/pages/category/[...slugs].vue -->
<script setup lang="ts">
const route = useRoute();
const { addKeys } = useSurrogateKeys();
addKeys(`category-${(route.params.slugs as string[]).at(-1)}`, 'plp');
</script>
3. (Optional) Add global keys via server middleware
If you want every response to carry a baseline key (e.g., storefront), create a server/middleware/ directory and add a file there:
// apps/storefront-unified-nuxt/server/middleware/surrogate-keys.ts
export default defineEventHandler((event) => {
event.node.res.setHeader('Surrogate-Key', 'storefront');
});
Page-level addKeys calls will append to whatever the middleware has already set.
Tagging with data from your backend
The route-based examples above use the URL slug to build keys. In many cases you will also want to tag responses with IDs coming from your commerce backend — for example, the numeric product ID or the category tree. You can do this by calling the helper after your data fetch resolves:
// Next.js — inside a Server Component or route handler
const product = await sdk.commerce.getProduct({ slug: params.slug });
const keys = [
`product-${product.id}`,
...product.categories.map((c) => `category-${c.id}`),
'pdp',
];
// Nuxt — inside <script setup>
const product = await sdk.commerce.getProduct({ slug: route.params.slug });
addKeys(
`product-${product.id}`,
...product.categories.map((c) => `category-${c.id}`),
'pdp',
);
This way, purging category-42 will invalidate not just the category listing page, but also every product page that belongs to that category.
Validating your setup
- Deploy your application (or run it in production mode locally).
- Open your browser's Network tab.
- Load a page that should carry surrogate keys.
- Inspect the response headers of the initial HTML document request.
The Surrogate-Key header is stripped by the CDN in production. To verify keys are being set correctly, either inspect headers when bypassing the CDN (e.g., hitting the origin directly) or use a staging environment with CDN disabled.
Purging keys from the Console
- In the Alokai Console, navigate to your instance.
- Open Caching Layer.
- Click CDN Cache.
- Click Purge keys.
- In the Purge keys input, enter one or more keys separated by spaces.
- Optionally, enable Soft purge to mark content as outdated (stale) instead of deleting it from caches. Soft-purged content can still be served while the CDN fetches a fresh version from the origin, reducing the impact on origin load.
- Click Purge.
Example inputs:
| Goal | Input |
|---|---|
| Purge a single product across all pages | product-123 |
| Purge an entire category | category-shoes |
| Purge all cache for a specific domain | store.example.com |
| Purge multiple keys at once | product-123 category-shoes homepage |
Purging typically propagates across the CDN within a few seconds.
Soft purge vs. hard purge
| Hard purge (default) | Soft purge | |
|---|---|---|
| Behavior | Immediately removes objects from cache. The next request triggers a full origin fetch. | Marks objects as stale. The CDN can still serve stale content while revalidating with the origin. |
| Origin impact | Higher — all requests for purged content hit the origin simultaneously. | Lower — stale content absorbs traffic while the origin is contacted in the background. |
| When to use | Content must be removed immediately (e.g., legal or compliance reasons). | Routine content updates where brief staleness is acceptable. |
Common use cases
Product or price update — Tag product detail and listing pages with product-{id}. When a product changes, purge that single key to refresh every page that references the product.
Deployment or global change — After a layout or configuration change, purge by domain key (e.g., store.example.com) to clear the entire storefront cache for that domain.
Locale-specific content refresh — Tag responses with a locale key like en-us. Purge the locale key when translations or region-specific content are updated.
Best practices
- Be specific. The more granular your keys, the more precisely you can target cache invalidation without over-purging.
- Prefer soft purge for routine updates to reduce origin load spikes.
- Use the automatic host key when you need a quick domain-wide cache clear without custom tagging.
- Plan your key taxonomy early. A consistent naming convention (e.g.,
entity-type-id) makes purging predictable and easy to automate. - Combine with cache rules. Surrogate key purging works alongside your existing caching rules — they control what gets cached and for how long, while key-based purging controls when cached content is removed.