Skip to content

Commit f7fd981

Browse files
igoramfclaude
andauthored
feat: enable CDN cache for all sales channels (#1562)
* feat: enable CDN cache for all sales channels * feat: enable CDN cache for all sales channels - Remove isDefautSalesChannel restriction from isCacheableSegment so non-default channels are cacheable (CDN varies by vtex_segment/VTEXSC) - Always persist VTEXSC cookie for ?sc=X requests regardless of cacheability, ensuring sales channel continuity across navigation - Remove unused PAGE_CACHE_ALLOWED_KEY import from vtex middleware Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * fix: guard non-default channels against caching when channelPrivacy is unknown Non-default sales channels are only cacheable when channelPrivacy is explicitly "public". If channelPrivacy is null/undefined (e.g. cold request), skip CDN cache to avoid serving a private channel response on first hit. * fix: skip Set-Cookie on cacheable responses to allow CDN caching Cloudflare does not cache responses with Set-Cookie headers. Guard both VTEXSC and vtex_segment cookies inside the !isCacheableSegment block so cacheable responses stay cookie-free and the CDN can cache them. Navigation continuity is preserved: the first cold request for a non-default channel is always non-cacheable (channelPrivacy unknown), so cookies are set there and carried by the browser on subsequent requests. * feat: enable CDN cache for all sales channels via cookie vary Remove channel restriction from isCacheableSegment — all non-private channels are now cacheable. Always set vtex_segment and VTEXSC on every response so the CDN (Cloudflare) can vary its cache key by them and serve the correct bucket per sales channel. Requires deco-cx/deco#XXXX (allowlist vtex_segment/VTEXSC in middleware). * fix: remove UTM checks from isCacheableSegment * fix: replace removed @deco/deco RequestInit import with local DecoRequestInit Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 0b95d39 commit f7fd981

3 files changed

Lines changed: 29 additions & 47 deletions

File tree

utils/http.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { type RequestInit } from "@deco/deco";
2-
import { fetchSafe } from "./fetch.ts";
1+
import { type DecoRequestInit, fetchSafe } from "./fetch.ts";
32

43
// Check if DEBUG_HTTP env var is set
54
const DEBUG_HTTP = Deno.env.get("DEBUG_HTTP") === "true";
@@ -25,7 +24,7 @@ export class HttpError extends Error {
2524
}
2625
}
2726

28-
export interface TypedRequestInit<T> extends Omit<RequestInit, "body"> {
27+
export interface TypedRequestInit<T> extends Omit<DecoRequestInit, "body"> {
2928
body: T;
3029
excludeFromSearchParams?: string[];
3130
templateMarker?: string;
@@ -82,7 +81,7 @@ export type ClientOf<T> = {
8281
searchParams?: infer Params;
8382
} ? (
8483
params: URLPatternParams<`/${path}`> & Params,
85-
init?: Omit<RequestInit, "body">,
84+
init?: Omit<DecoRequestInit, "body">,
8685
) => Promise<TypedResponse<ResBody>>
8786
: never
8887
: never;
@@ -241,7 +240,7 @@ export const createHttpClient = <T>(
241240
}
242241
return (
243242
params: Record<string, string | number | string[] | number[]>,
244-
init?: RequestInit & {
243+
init?: DecoRequestInit & {
245244
excludeFromSearchParams?: string[];
246245
templateMarker?: string;
247246
},

vtex/middleware.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getCookies } from "std/http/cookie.ts";
2-
import { PAGE_CACHE_ALLOWED_KEY, PAGE_DIRTY_KEY } from "@deco/deco/blocks";
2+
import { PAGE_DIRTY_KEY } from "@deco/deco/blocks";
33
import { AppMiddlewareContext } from "./mod.ts";
44
import {
55
getISCookiesFromBag,
@@ -45,10 +45,5 @@ export const middleware = (
4545
);
4646
}
4747

48-
// PAGE_CACHE_ALLOWED_KEY: opts in to CDN page caching (VTEX-only)
49-
if (cacheable) {
50-
ctx.bag.set(PAGE_CACHE_ALLOWED_KEY, true);
51-
}
52-
5348
return ctx.next!();
5449
};

vtex/utils/segment.ts

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -61,24 +61,13 @@ export const isAnonymous = (
6161
!regionId;
6262
};
6363

64-
/**
65-
* Checks if the segment is cacheable for CDN purposes.
66-
* By default, uses isAnonymous (UTMs affect cacheability because prices
67-
* can vary by utm_source). With removeUTMFromCacheKey, UTMs are ignored
68-
* (opt-in for stores that don't vary prices by UTM).
69-
*/
7064
export const isCacheableSegment = (ctx: AppContext) => {
7165
const payload = getSegmentFromBag(ctx)?.payload;
7266
if (payload?.channelPrivacy === "private") return false;
7367

74-
if (ctx.advancedConfigs?.removeUTMFromCacheKey) {
75-
if (!payload) return true;
76-
const { campaigns, channel, priceTables, regionId } = payload;
77-
return !campaigns &&
78-
(!channel || isDefautSalesChannel(ctx, channel)) &&
79-
!priceTables && !regionId;
80-
}
81-
return isAnonymous(ctx);
68+
if (!payload) return true;
69+
const { campaigns, priceTables, regionId } = payload;
70+
return !campaigns && !priceTables && !regionId;
8271
};
8372

8473
const setSegmentInBag = (ctx: AppContext, data: WrappedSegment) =>
@@ -259,28 +248,27 @@ export const setSegmentBag = (
259248
const token = serialize(segment);
260249
setSegmentInBag(ctx, { payload: segment, token });
261250

262-
// Skip Set-Cookie when the segment only differs by UTMs.
263-
// UTMs don't affect page content, so the response can still be cached.
264-
// Only set cookies when content-affecting fields differ (campaigns,
265-
// non-default sales channel, price tables, region).
266-
if (!isCacheableSegment(ctx)) {
267-
if (segmentFromRequest.channel) {
268-
setCookie(ctx.response.headers, {
269-
value: `sc=${segmentFromRequest.channel}`,
270-
name: SALES_CHANNEL_COOKIE,
271-
path: "/",
272-
secure: true,
273-
});
274-
}
251+
// Always persist sales channel when it comes from request params so the
252+
// browser carries it across navigation. The CDN varies its cache key by
253+
// VTEXSC, so setting this cookie does not prevent CDN caching.
254+
if (segmentFromRequest.channel) {
255+
setCookie(ctx.response.headers, {
256+
value: `sc=${segmentFromRequest.channel}`,
257+
name: SALES_CHANNEL_COOKIE,
258+
path: "/",
259+
secure: true,
260+
});
261+
}
275262

276-
if (vtex_segment !== token) {
277-
setCookie(ctx.response.headers, {
278-
value: token,
279-
name: SEGMENT_COOKIE_NAME,
280-
path: "/",
281-
secure: true,
282-
httpOnly: true,
283-
});
284-
}
263+
// Always keep vtex_segment fresh so the CDN vary key stays accurate.
264+
// The CDN varies by this cookie, so setting it does not prevent caching.
265+
if (vtex_segment !== token) {
266+
setCookie(ctx.response.headers, {
267+
value: token,
268+
name: SEGMENT_COOKIE_NAME,
269+
path: "/",
270+
secure: true,
271+
httpOnly: true,
272+
});
285273
}
286274
};

0 commit comments

Comments
 (0)