Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 2x 14x 14x 33x 24x 14x 14x 14x | // Edge cache-key normalization.
//
// Tracking / click-ID params never change the rendered HTML, but they ARE unique
// per click. Left in the cache key, each paid-ad click (Instagram/Facebook append
// a fresh fbclid + utm_* + brid to every URL) becomes its own cache entry → 100%
// edge-cache MISS → every click triggers a live SSR→API fetch → the API saturates
// and the 8s client timeout fires → HTTP 503 storm (prod incident 2026-06-20).
//
// Stripping these collapses all ad clicks onto ONE `/` entry: the first renders +
// caches, the rest are edge HITs that never touch the API. Functional params
// (search, facet filters, page) are preserved, so listing pages still vary.
// Known click-ID params (no shared prefix). utm_* is matched by prefix below.
const TRACKING_PARAMS = new Set([
"fbclid", // Facebook / Instagram
"brid", // Meta ad click param (seen on m.facebook.com → IG ad referrals)
"gclid",
"gad_source",
"gbraid",
"wbraid", // Google Ads
"msclkid", // Microsoft / Bing
"dclid", // DoubleClick
"ttclid", // TikTok
"twclid", // Twitter / X
"igshid", // Instagram share id
"li_fat_id", // LinkedIn
"epik", // Pinterest
"mc_cid",
"mc_eid", // Mailchimp
"yclid", // Yandex
"scid", // generic source click id
"_branch_match_id", // Branch
]);
/**
* Build the canonical edge cache-key URL for a request:
* - drops `utm_*` and known click-ID params (tracking-only, never affect render),
* - sorts remaining params so `?a=1&b=2` and `?b=2&a=1` share one entry,
* - appends `__v=<deployVersion>` so each deploy starts a clean cache namespace.
*
* Only the cache KEY is normalized; the real request URL (with tracking params)
* still reaches the page and client-side analytics.
*/
export function buildCacheKeyUrl(
requestUrl: string,
deployVersion: string,
): string {
const u = new URL(requestUrl);
// Snapshot keys before deleting — mutating the live iterator would skip entries.
for (const key of [...u.searchParams.keys()]) {
if (key.startsWith("utm_") || TRACKING_PARAMS.has(key)) {
u.searchParams.delete(key);
}
}
u.searchParams.sort();
u.searchParams.set("__v", deployVersion);
return u.toString();
}
|