[{"data":1,"prerenderedAt":1139},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate":1130},{"id":4,"title":5,"body":6,"description":1110,"extension":1111,"meta":1112,"navigation":1123,"path":1124,"seo":1125,"stem":1128,"__hash__":1129},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate\u002Findex.md","Normalizing Cache Keys to Raise Hit Rate",{"type":7,"value":8,"toc":1094},"minimark",[9,14,29,45,202,207,265,269,275,288,306,312,383,387,392,395,694,697,701,712,716,719,757,761,771,885,889,892,896,912,916,942,946,952,962,968,974,978,994,1007,1016,1025,1034,1043,1052,1056,1079,1084,1087,1090],[10,11,13],"h1",{"id":12},"how-to-normalize-cache-keys-to-raise-cdn-hit-rate","How to Normalize Cache Keys to Raise CDN Hit Rate",[15,16,17,18,23,24,28],"p",{},"This guide belongs to ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002F","CDN Edge Caching Configuration"," in ",[19,25,27],{"href":26},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",". A CDN stores and looks up responses by a cache key — by default, usually the full URL including the query string, plus the host and sometimes selected headers. Two requests that should receive the same response but differ in any part of the key are cached separately. Each copy must be fetched from the origin at least once per location, and copies that are requested rarely expire before they are reused.",[15,30,31,32,36,37,40,41,44],{},"The usual culprits are mundane: ",[33,34,35],"code",{},"?utm_source=newsletter"," on a campaign link, ",[33,38,39],{},"?fbclid=…"," appended by social platforms, parameters in a different order, mixed-case paths, trailing slashes, and ",[33,42,43],{},"Vary"," headers on values that do not change the content. On marketing-heavy sites, a large fraction of HTML requests carry at least one tracking parameter, so every campaign spreads one page across thousands of cache entries — and every one of those visitors pays origin TTFB.",[15,46,47,122,140,141,55,145],{},[48,49,55,56,55,63,55,67,55,70,55,88,55,96,55,102,55,110,55,117],"svg",{"viewBox":50,"width":51,"role":52,"ariaLabel":53,"style":54},"0 0 760 149","100%","img","Steps that turn a raw request URL into a normalised cache key by removing tracking parameters, sorting, lowercasing and dropping irrelevant headers.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[57,58],"rect",{"className":59,"x":61,"y":61,"width":51,"height":51,"fill":62},[60],"svg-canvas","0","#ffffff",[64,65,66],"title",{},"Normalising a request into a cache key",[68,69,53],"desc",{},[71,72,73],"defs",{},[74,75,82],"marker",{"id":76,"viewBox":77,"refX":78,"refY":79,"markerWidth":80,"markerHeight":80,"orient":81},"fa8b57910b","0 0 10 10","9","5","7","auto-start-reverse",[83,84],"path",{"d":85,"fill":86,"style":87},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[57,89],{"x":90,"y":90,"width":91,"height":92,"rx":93,"fill":94,"stroke":86,"style":95},"1","758","147","10","none","stroke-opacity:0.18",[97,98,66],"text",{"x":99,"y":100,"fill":86,"style":101},"28.0","34.0","font-size:16px;font-weight:700",[57,103],{"x":99,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":108,"style":109},"56.0","108.8","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[97,111,116],{"x":112,"y":113,"fill":86,"style":114,"textAnchor":115},"82.4","78.0","font-size:13px;font-weight:700","middle","Raw URL",[97,118,121],{"x":112,"y":119,"fill":86,"style":120,"textAnchor":115},"96.0","font-size:12px","params + utm_",[123,124,55,125],"em",{},[57,126,55,133,55,137],{"x":127,"y":104,"width":128,"height":129,"rx":107,"fill":130,"stroke":131,"style":132},"176.8",108.8,71,"#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[97,134,136],{"x":135,"y":113,"fill":86,"style":114,"text-anchor":115},"231.2","Drop tracking",[97,138,139],{"x":135,"y":119,"fill":86,"style":120,"text-anchor":115},"utm_",", gclid, ",[97,142,144],{"x":135,"y":143,"fill":86,"style":120,"text-anchor":115},"112.0","fbclid",[57,146,55,148,55,152,55,155],{"x":147,"y":104,"width":128,"height":129,"rx":107,"fill":108,"stroke":108,"style":109},"325.6",[97,149,151],{"x":150,"y":113,"fill":86,"style":114,"text-anchor":115},"380.0","Sort params",[97,153,154],{"x":150,"y":119,"fill":86,"style":120,"text-anchor":115},"?a=1&b=2",[57,156,55,158,55,162,55,165,55,168],{"x":157,"y":104,"width":128,"height":129,"rx":107,"fill":108,"stroke":108,"style":109},"474.4",[97,159,161],{"x":160,"y":113,"fill":86,"style":114,"text-anchor":115},"528.8","Normalise path",[97,163,164],{"x":160,"y":119,"fill":86,"style":120,"text-anchor":115},"case + trailing",[97,166,167],{"x":160,"y":143,"fill":86,"style":120,"text-anchor":115},"slash",[57,169,55,171,55,175,55,178,55,181],{"x":170,"y":104,"width":128,"height":129,"rx":107,"fill":108,"stroke":108,"style":109},"623.2",[97,172,174],{"x":173,"y":113,"fill":86,"style":114,"text-anchor":115},"677.6","Cache key",[97,176,177],{"x":173,"y":119,"fill":86,"style":120,"text-anchor":115},"one entry per",[97,179,180],{"x":173,"y":143,"fill":86,"style":120,"text-anchor":115},"page",[182,183,55,190],"line",{"x1":184,"y1":185,"x2":186,"y2":185,"stroke":86,"stroke-width":187,"style":188,"marker-end":189},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa8b57910b)",[182,191,55,194],{"x1":192,"y1":185,"x2":193,"y2":185,"stroke":86,"stroke-width":187,"style":188,"marker-end":189},"285.6","323.6",[182,195,55,198],{"x1":196,"y1":185,"x2":197,"y2":185,"stroke":86,"stroke-width":187,"style":188,"marker-end":189},"434.4","472.4",[182,199,55],{"x1":200,"y1":185,"x2":201,"y2":185,"stroke":86,"stroke-width":187,"style":188,"marker-end":189},"583.2","621.2",[203,204,206],"h2",{"id":205},"rapid-diagnosis","Rapid Diagnosis",[208,209,210,218,240,246],"ul",{},[211,212,213,217],"li",{},[214,215,216],"strong",{},"Sample CDN logs for one popular page."," Count distinct cache keys (full URLs) for the same path. Dozens or hundreds of variants indicate fragmentation.",[211,219,220,223,224,227,228,227,231,227,233,227,236,239],{},[214,221,222],{},"Group query parameters by name"," across HTML requests. Tracking parameters (",[33,225,226],{},"utm_*",", ",[33,229,230],{},"gclid",[33,232,144],{},[33,234,235],{},"msclkid",[33,237,238],{},"mc_eid",") are typically the largest group.",[211,241,242,245],{},[214,243,244],{},"Check hit ratio by \"has query string\"."," A large gap between URLs with and without query strings is a strong signal.",[211,247,248,55,254,227,257,260,261,264],{},[214,249,250,251,253],{},"Inspect ",[33,252,43],{}," on HTML and API responses.",[33,255,256],{},"Vary: User-Agent",[33,258,259],{},"Vary: Cookie"," or ",[33,262,263],{},"Vary: Accept-Language"," multiply entries.",[203,266,268],{"id":267},"root-cause-analysis","Root Cause Analysis",[15,270,271,274],{},[214,272,273],{},"1. Tracking parameters."," Added by marketing tools and ad platforms, they never change the response but always change the key.",[15,276,277,55,280,283,284,287],{},[214,278,279],{},"2. Parameter order and casing.",[33,281,282],{},"?color=red&size=m"," and ",[33,285,286],{},"?size=m&color=red"," are different keys unless sorted.",[15,289,290,55,293,296,297,227,300,296,302,305],{},[214,291,292],{},"3. Path inconsistencies.",[33,294,295],{},"\u002FShoes"," vs ",[33,298,299],{},"\u002Fshoes",[33,301,299],{},[33,303,304],{},"\u002Fshoes\u002F",", and duplicate slashes.",[15,307,308,311],{},[214,309,310],{},"4. Overly broad Vary."," Headers with many distinct values, especially user agent and cookie, create per-browser or per-user entries.",[15,313,314],{},[48,315,55,318,55,321,55,324,55,326,55,329,55,331,55,338,55,345,55,350,55,354,55,359,55,363,55,367,55,371,55,374,55,378,55,381,55],{"viewBox":316,"width":51,"role":52,"ariaLabel":317,"style":54},"0 0 760 194","Bar chart of distinct cache keys observed for one landing page under default keys and after each normalisation step.",[57,319],{"className":320,"x":61,"y":61,"width":51,"height":51,"fill":62},[60],[64,322,323],{},"Cache entries per landing page during a campaign",[68,325,317],{},[57,327],{"x":90,"y":90,"width":91,"height":328,"rx":93,"fill":94,"stroke":86,"style":95},"192",[97,330,323],{"x":99,"y":100,"fill":86,"style":101},[97,332,337],{"x":333,"y":334,"fill":86,"style":335,"textAnchor":336},"180.1","70.0","font-size:13px","end","Default (full URL)",[57,339],{"x":340,"y":104,"width":341,"height":342,"rx":343,"fill":130,"stroke":131,"style":344},"192.1","475.9","19","3","fill-opacity:0.7;stroke-opacity:0.9",[97,346,349],{"x":347,"y":334,"fill":86,"style":348},"674.0","font-size:12px;font-weight:600","1840",[97,351,353],{"x":333,"y":352,"fill":86,"style":335,"textAnchor":336},"101.0","Tracking params removed",[57,355],{"x":340,"y":356,"width":357,"height":342,"rx":343,"fill":108,"stroke":108,"style":358},"87.0","3.1","fill-opacity:0.55;stroke-opacity:0.9",[97,360,362],{"x":361,"y":352,"fill":86,"style":348},"201.2","12",[97,364,366],{"x":333,"y":365,"fill":86,"style":335,"textAnchor":336},"132.0","+ params sorted",[57,368],{"x":340,"y":369,"width":370,"height":342,"rx":343,"fill":108,"stroke":108,"style":358},"118.0","2.0",[97,372,80],{"x":373,"y":365,"fill":86,"style":348},"200.1",[97,375,377],{"x":333,"y":376,"fill":86,"style":335,"textAnchor":336},"163.0","+ path normalised",[57,379],{"x":340,"y":380,"width":370,"height":342,"rx":343,"fill":108,"stroke":108,"style":358},"149.0",[97,382,343],{"x":373,"y":376,"fill":86,"style":348},[203,384,386],{"id":385},"step-by-step-resolution","Step-by-Step Resolution",[388,389,391],"h3",{"id":390},"_1-strip-tracking-parameters-from-the-key-not-the-url","1. Strip tracking parameters from the key (not the URL)",[15,393,394],{},"Remove marketing parameters from the cache key while leaving them in the request the origin or analytics sees, so attribution keeps working.",[396,397,402],"pre",{"className":398,"code":399,"language":400,"meta":401,"style":401},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Cloudflare Worker: normalised cache key, original URL untouched.\nconst TRACKING = \u002F^(utm_\\w+|gclid|fbclid|msclkid|mc_eid|_ga|ref)$\u002Fi;\nfunction cacheKeyFor(request) {\n  const url = new URL(request.url);\n  for (const k of [...url.searchParams.keys()]) if (TRACKING.test(k)) url.searchParams.delete(k);\n  url.searchParams.sort();\n  url.pathname = url.pathname.toLowerCase().replace(\u002F\\\u002F+$\u002F, '') || '\u002F';\n  return url.toString();\n}\nconst response = await fetch(request, { cf: { cacheKey: cacheKeyFor(request) } });\n\u002F\u002F trade-off: lowercasing paths is only safe if your origin treats paths\n\u002F\u002F case-insensitively. If \u002FDocs and \u002Fdocs are different pages, skip that step.\n","javascript","",[33,403,404,412,480,500,520,575,587,637,651,657,682,688],{"__ignoreMap":401},[405,406,408],"span",{"class":182,"line":407},1,[405,409,411],{"class":410},"sjfSM","\u002F\u002F Cloudflare Worker: normalised cache key, original URL untouched.\n",[405,413,415,419,423,426,430,433,436,439,442,444,447,449,451,453,455,457,459,462,464,467,470,473,476],{"class":182,"line":414},2,[405,416,418],{"class":417},"sPARh","const",[405,420,422],{"class":421},"sPXB4"," TRACKING",[405,424,425],{"class":417}," =",[405,427,429],{"class":428},"sZ8jY"," \u002F",[405,431,432],{"class":417},"^",[405,434,435],{"class":428},"(utm_",[405,437,438],{"class":421},"\\w",[405,440,441],{"class":417},"+|",[405,443,230],{"class":428},[405,445,446],{"class":417},"|",[405,448,144],{"class":428},[405,450,446],{"class":417},[405,452,235],{"class":428},[405,454,446],{"class":417},[405,456,238],{"class":428},[405,458,446],{"class":417},[405,460,461],{"class":428},"_ga",[405,463,446],{"class":417},[405,465,466],{"class":428},"ref)",[405,468,469],{"class":417},"$",[405,471,472],{"class":428},"\u002F",[405,474,475],{"class":417},"i",[405,477,479],{"class":478},"saISM",";\n",[405,481,483,486,490,493,497],{"class":182,"line":482},3,[405,484,485],{"class":417},"function",[405,487,489],{"class":488},"smZ65"," cacheKeyFor",[405,491,492],{"class":478},"(",[405,494,496],{"class":495},"sQw3B","request",[405,498,499],{"class":478},") {\n",[405,501,503,506,509,511,514,517],{"class":182,"line":502},4,[405,504,505],{"class":417},"  const",[405,507,508],{"class":421}," url",[405,510,425],{"class":417},[405,512,513],{"class":417}," new",[405,515,516],{"class":488}," URL",[405,518,519],{"class":478},"(request.url);\n",[405,521,523,526,529,531,534,537,540,543,546,549,552,555,557,560,563,566,569,572],{"class":182,"line":522},5,[405,524,525],{"class":417},"  for",[405,527,528],{"class":478}," (",[405,530,418],{"class":417},[405,532,533],{"class":421}," k",[405,535,536],{"class":417}," of",[405,538,539],{"class":478}," [",[405,541,542],{"class":417},"...",[405,544,545],{"class":478},"url.searchParams.",[405,547,548],{"class":488},"keys",[405,550,551],{"class":478},"()]) ",[405,553,554],{"class":417},"if",[405,556,528],{"class":478},[405,558,559],{"class":421},"TRACKING",[405,561,562],{"class":478},".",[405,564,565],{"class":488},"test",[405,567,568],{"class":478},"(k)) url.searchParams.",[405,570,571],{"class":488},"delete",[405,573,574],{"class":478},"(k);\n",[405,576,578,581,584],{"class":182,"line":577},6,[405,579,580],{"class":478},"  url.searchParams.",[405,582,583],{"class":488},"sort",[405,585,586],{"class":478},"();\n",[405,588,590,593,596,599,602,605,608,610,612,616,619,621,623,626,629,632,635],{"class":182,"line":589},7,[405,591,592],{"class":478},"  url.pathname ",[405,594,595],{"class":417},"=",[405,597,598],{"class":478}," url.pathname.",[405,600,601],{"class":488},"toLowerCase",[405,603,604],{"class":478},"().",[405,606,607],{"class":488},"replace",[405,609,492],{"class":478},[405,611,472],{"class":428},[405,613,615],{"class":614},"sn2CQ","\\\u002F",[405,617,618],{"class":417},"+$",[405,620,472],{"class":428},[405,622,227],{"class":478},[405,624,625],{"class":428},"''",[405,627,628],{"class":478},") ",[405,630,631],{"class":417},"||",[405,633,634],{"class":428}," '\u002F'",[405,636,479],{"class":478},[405,638,640,643,646,649],{"class":182,"line":639},8,[405,641,642],{"class":417},"  return",[405,644,645],{"class":478}," url.",[405,647,648],{"class":488},"toString",[405,650,586],{"class":478},[405,652,654],{"class":182,"line":653},9,[405,655,656],{"class":478},"}\n",[405,658,660,662,665,667,670,673,676,679],{"class":182,"line":659},10,[405,661,418],{"class":417},[405,663,664],{"class":421}," response",[405,666,425],{"class":417},[405,668,669],{"class":417}," await",[405,671,672],{"class":488}," fetch",[405,674,675],{"class":478},"(request, { cf: { cacheKey: ",[405,677,678],{"class":488},"cacheKeyFor",[405,680,681],{"class":478},"(request) } });\n",[405,683,685],{"class":182,"line":684},11,[405,686,687],{"class":410},"\u002F\u002F trade-off: lowercasing paths is only safe if your origin treats paths\n",[405,689,691],{"class":182,"line":690},12,[405,692,693],{"class":410},"\u002F\u002F case-insensitively. If \u002FDocs and \u002Fdocs are different pages, skip that step.\n",[15,695,696],{},"Expected outcome: all campaign variants of a page share one cache entry.",[388,698,700],{"id":699},"_2-allow-list-parameters-for-api-and-search-routes","2. Allow-list parameters for API and search routes",[15,702,703,704,227,707,227,709,711],{},"For routes where parameters matter, include only the ones the origin actually reads (for example ",[33,705,706],{},"q",[33,708,180],{},[33,710,583],{},") and drop everything else from the key.",[388,713,715],{"id":714},"_3-redirect-inconsistent-paths-at-the-edge","3. Redirect inconsistent paths at the edge",[15,717,718],{},"Issue a single 301 from non-canonical paths (uppercase, missing or extra trailing slash) to the canonical one, cached at the edge, so variants converge on one URL.",[396,720,724],{"className":721,"code":722,"language":723,"meta":401,"style":401},"language-nginx shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Canonicalise trailing slash with a single cached redirect.\nrewrite ^([^.]*[^\u002F])$ $1\u002F permanent;\n# trade-off: redirects add a round trip for the visitor who followed the\n# non-canonical link. Fix internal links so redirects are rare.\n","nginx",[33,725,726,731,747,752],{"__ignoreMap":401},[405,727,728],{"class":182,"line":407},[405,729,730],{"class":410},"# Canonicalise trailing slash with a single cached redirect.\n",[405,732,733,736,739,742,745],{"class":182,"line":414},[405,734,735],{"class":417},"rewrite",[405,737,738],{"class":428}," ^([^.]*[^\u002F])$",[405,740,741],{"class":478}," $1\u002F ",[405,743,744],{"class":417},"permanent",[405,746,479],{"class":478},[405,748,749],{"class":182,"line":482},[405,750,751],{"class":410},"# trade-off: redirects add a round trip for the visitor who followed the\n",[405,753,754],{"class":182,"line":502},[405,755,756],{"class":410},"# non-canonical link. Fix internal links so redirects are rare.\n",[388,758,760],{"id":759},"_4-replace-broad-vary-headers-with-explicit-keys","4. Replace broad Vary headers with explicit keys",[15,762,763,764,766,767,562],{},"Instead of ",[33,765,256],{},", derive a coarse device class at the edge if the HTML truly differs, and put that in the key. Better still, serve one responsive document. See ",[19,768,770],{"href":769},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fvary-header-pitfalls-that-destroy-cdn-hit-rate\u002F","Vary header pitfalls that destroy CDN hit rate",[15,772,773],{},[48,774,55,777,55,780,55,783,55,785,55,788,55,790,55,795,55,802,55,806,55,810,55,813,55,817,55,821,55,825,55,827,55,830,55,832,55,835,55,838,55,842,55,844,55,847,55,849,55,852,55,855,55,859,55,861,55,864,55,866,55,868,55,871,55,875,55,877,55,880,55,882,55],{"viewBox":775,"width":51,"role":52,"ariaLabel":776,"style":54},"0 0 760 228","Recommended cache key normalisation for HTML pages, search pages, API routes and static assets.",[57,778],{"className":779,"x":61,"y":61,"width":51,"height":51,"fill":62},[60],[64,781,782],{},"Key normalisation rules by route type",[68,784,776],{},[57,786],{"x":90,"y":90,"width":91,"height":787,"rx":93,"fill":94,"stroke":86,"style":95},"226",[97,789,782],{"x":99,"y":100,"fill":86,"style":101},[57,791],{"x":99,"y":104,"width":792,"height":793,"rx":61,"fill":86,"stroke":86,"style":794},"172.7","30.0","fill-opacity:0.06;stroke-opacity:0.4",[97,796,801],{"x":797,"y":798,"fill":86,"style":799,"textAnchor":800},"38.0","75.5","font-size:12.5px;font-weight:700","start","Route type",[57,803],{"x":804,"y":104,"width":805,"height":793,"rx":61,"fill":86,"stroke":86,"style":794},"200.7","265.7",[97,807,809],{"x":808,"y":798,"fill":86,"style":799,"textAnchor":115},"333.5","Query params in key",[57,811],{"x":812,"y":104,"width":805,"height":793,"rx":61,"fill":86,"stroke":86,"style":794},"466.3",[97,814,816],{"x":815,"y":798,"fill":86,"style":799,"textAnchor":115},"599.2","Other normalisation",[57,818],{"x":99,"y":819,"width":792,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},"86.0","stroke-opacity:0.35",[97,822,824],{"x":797,"y":823,"fill":86,"style":799,"textAnchor":800},"105.5","Marketing \u002F content HTML",[57,826],{"x":804,"y":819,"width":805,"height":793,"rx":61,"fill":108,"stroke":108,"style":109},[97,828,829],{"x":808,"y":823,"fill":86,"style":120,"textAnchor":115},"none (strip all)",[57,831],{"x":812,"y":819,"width":805,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},[97,833,834],{"x":815,"y":823,"fill":86,"style":120,"textAnchor":115},"lowercase path, trailing slash",[57,836],{"x":99,"y":837,"width":792,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},"116.0",[97,839,841],{"x":797,"y":840,"fill":86,"style":799,"textAnchor":800},"135.5","Search \u002F listing HTML",[57,843],{"x":804,"y":837,"width":805,"height":793,"rx":61,"fill":86,"stroke":86,"style":794},[97,845,846],{"x":808,"y":840,"fill":86,"style":120,"textAnchor":115},"allow-list q, page, sort",[57,848],{"x":812,"y":837,"width":805,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},[97,850,851],{"x":815,"y":840,"fill":86,"style":120,"textAnchor":115},"sort params",[57,853],{"x":99,"y":854,"width":792,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},"146.0",[97,856,858],{"x":797,"y":857,"fill":86,"style":799,"textAnchor":800},"165.5","API GET routes",[57,860],{"x":804,"y":854,"width":805,"height":793,"rx":61,"fill":86,"stroke":86,"style":794},[97,862,863],{"x":808,"y":857,"fill":86,"style":120,"textAnchor":115},"allow-list documented params",[57,865],{"x":812,"y":854,"width":805,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},[97,867,851],{"x":815,"y":857,"fill":86,"style":120,"textAnchor":115},[57,869],{"x":99,"y":870,"width":792,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},"176.0",[97,872,874],{"x":797,"y":873,"fill":86,"style":799,"textAnchor":800},"195.5","Hashed static assets",[57,876],{"x":804,"y":870,"width":805,"height":793,"rx":61,"fill":108,"stroke":108,"style":109},[97,878,879],{"x":808,"y":873,"fill":86,"style":120,"textAnchor":115},"ignore query entirely",[57,881],{"x":812,"y":870,"width":805,"height":793,"rx":61,"fill":94,"stroke":86,"style":820},[97,883,884],{"x":815,"y":873,"fill":86,"style":120,"textAnchor":115},"immutable, no Vary",[203,886,888],{"id":887},"verification","Verification",[15,890,891],{},"Request a page with several combinations of tracking parameters and parameter orders; after the first request, all should return a cache HIT. In CDN analytics, the number of distinct keys per popular path should collapse and the HTML hit ratio should rise — on campaign-heavy sites, often from below 40% to above 85%. Confirm analytics still records campaign parameters, since the origin and client still see the full URL.",[203,893,895],{"id":894},"worked-example-a-campaign-launch","Worked Example: A Campaign Launch",[15,897,898,899,227,902,227,905,908,909,911],{},"A retailer launched a campaign with links carrying ",[33,900,901],{},"utm_source",[33,903,904],{},"utm_medium",[33,906,907],{},"utm_campaign"," and per-recipient ",[33,910,238],{}," values. The landing page's hit ratio dropped from 92% to 9% during the send, and origin CPU spiked; TTFB p75 for campaign visitors was 840ms. After adding key normalisation that stripped tracking parameters, the next campaign's landing page held a 94% hit ratio, campaign visitors' TTFB p75 was 70ms, and the origin barely noticed the traffic spike.",[203,913,915],{"id":914},"common-mistakes","Common Mistakes",[208,917,918,924,930,936],{},[211,919,920,923],{},[214,921,922],{},"Removing parameters from the URL instead of the key."," That breaks analytics attribution; normalise only the key.",[211,925,926,929],{},[214,927,928],{},"Stripping parameters the origin uses."," Use allow-lists on routes where parameters change content.",[211,931,932,935],{},[214,933,934],{},"Lowercasing case-sensitive paths."," Check origin behaviour first.",[211,937,938,941],{},[214,939,940],{},"Ignoring HTML caching altogether."," Key normalisation only matters if HTML is cacheable; many sites must fix that first.",[203,943,945],{"id":944},"edge-cases","Edge Cases",[15,947,948,951],{},[214,949,950],{},"Signed URLs."," Image and file URLs with signature parameters must keep them in the key, or one user's signed URL could be served to another.",[15,953,954,957,958,961],{},[214,955,956],{},"Hash fragments."," Fragments (",[33,959,960],{},"#section",") never reach the server and do not affect keys.",[15,963,964,967],{},[214,965,966],{},"Single-page apps."," Client-side routing may append parameters that the server ignores; normalise them out of HTML keys.",[15,969,970,973],{},[214,971,972],{},"Multiple CDNs."," Each layer (a CDN in front of another CDN or a load balancer cache) has its own key; normalise consistently at the outermost layer.",[203,975,977],{"id":976},"faq","FAQ",[979,980,983,987],"details",{"className":981},[982],"faq-item",[984,985,986],"summary",{},"Will stripping utm parameters break Google Analytics?",[15,988,989,990,993],{},"Not if you strip them only from the cache key. The browser still loads the original URL, so client-side analytics reads the parameters from ",[33,991,992],{},"location.search"," as usual.",[979,995,997,1000],{"className":996},[982],[984,998,999],{},"Should static assets include query strings in the key?",[15,1001,1002,1003,1006],{},"For content-hashed assets, no — the hash is in the filename. Some build setups use ",[33,1004,1005],{},"?v=123"," for cache busting; if so, keep that parameter in the key and drop the rest.",[979,1008,1010,1013],{"className":1009},[982],[984,1011,1012],{},"How do I find which parameters my origin actually uses?",[15,1014,1015],{},"Search the server code for query parameter reads per route, and cross-check with access logs: parameters that never change the response body (same ETag) for the same path can be dropped from the key.",[979,1017,1019,1022],{"className":1018},[982],[984,1020,1021],{},"Does normalisation help with bot traffic?",[15,1023,1024],{},"Yes. Crawlers and monitoring tools often append random parameters; normalising them out prevents bots from filling the cache with useless entries and from bypassing it to hit your origin.",[979,1026,1028,1031],{"className":1027},[982],[984,1029,1030],{},"Is the cache key configurable without edge compute?",[15,1032,1033],{},"Most CDNs offer cache-key settings in their configuration UI or rules engine — query string allow\u002Fdeny lists, header inclusion, case handling. Edge compute is only needed for logic beyond those options.",[979,1035,1037,1040],{"className":1036},[982],[984,1038,1039],{},"How do I roll out key normalisation safely?",[15,1041,1042],{},"Start with HTML routes that take no meaningful parameters (marketing and content pages), where stripping every parameter is safe. Monitor for any page that renders differently with and without a parameter before extending to listing and search routes with allow-lists. Keep a per-route switch so a mistake can be reverted without touching the rest of the configuration.",[979,1044,1046,1049],{"className":1045},[982],[984,1047,1048],{},"What hit ratio should HTML reach after normalisation?",[15,1050,1051],{},"For public content pages with tag-based purging and multi-minute TTLs, 85–95% at the edge is typical. Lower numbers usually point to remaining fragmentation, cookies forcing bypass, or TTLs too short for the traffic level per location — which tiered caching addresses.",[203,1053,1055],{"id":1054},"related","Related",[208,1057,1058,1065,1072],{},[211,1059,1060,1064],{},[19,1061,1063],{"href":1062},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fdiagnosing-cdn-cache-misses-from-response-headers\u002F","Diagnosing CDN cache misses from response headers"," — confirming keys behave as intended.",[211,1066,1067,1071],{},[19,1068,1070],{"href":1069},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Ftiered-caching-and-origin-shield\u002F","Tiered caching and origin shield"," — the next lever after key normalisation.",[211,1073,1074,1078],{},[19,1075,1077],{"href":1076},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fpersonalizing-cached-pages-at-the-edge\u002F","Personalizing cached pages at the edge"," — designing variant keys deliberately.",[1080,1081,1083],"script",{"type":1082},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Normalize Cache Keys to Raise CDN Hit Rate\",\n  \"description\": \"How cache-key fragmentation silently lowers CDN hit rates, and the normalisation rules that collapse variants without serving wrong content.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Strip tracking parameters from the key (not the URL)\",\n      \"text\": \"Remove marketing parameters from the cache key while leaving them in the request the origin or analytics sees, so attribution keeps working.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Allow-list parameters for API and search routes\",\n      \"text\": \"For routes where parameters matter, include only the ones the origin actually reads (for example q, page, sort) and drop everything else from the key.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Redirect inconsistent paths at the edge\",\n      \"text\": \"Issue a single 301 from non-canonical paths (uppercase, missing or extra trailing slash) to the canonical one, cached at the edge, so variants converge on one URL.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Replace broad Vary headers with explicit keys\",\n      \"text\": \"Instead of Vary: User-Agent, derive a coarse device class at the edge if the HTML truly differs, and put that in the key.\"\n    }\n  ]\n}\n",[1080,1085,1086],{"type":1082},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Normalize Cache Keys to Raise CDN Hit Rate\",\n  \"description\": \"How cache-key fragmentation silently lowers CDN hit rates, and the normalisation rules that collapse variants without serving wrong content.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate\u002F\"\n  }\n}\n",[1080,1088,1089],{"type":1082},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Advanced Caching Strategies & CDN Architecture\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"CDN Edge Caching Configuration\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Normalizing Cache Keys to Raise Hit Rate\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate\u002F\"\n    }\n  ]\n}\n",[1091,1092,1093],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sn2CQ, html code.shiki .sn2CQ{--shiki-default:#024C1A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold;--shiki-light:#024C1A;--shiki-light-font-weight:bold}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":401,"searchDepth":414,"depth":414,"links":1095},[1096,1097,1098,1104,1105,1106,1107,1108,1109],{"id":205,"depth":414,"text":206},{"id":267,"depth":414,"text":268},{"id":385,"depth":414,"text":386,"children":1099},[1100,1101,1102,1103],{"id":390,"depth":482,"text":391},{"id":699,"depth":482,"text":700},{"id":714,"depth":482,"text":715},{"id":759,"depth":482,"text":760},{"id":887,"depth":414,"text":888},{"id":894,"depth":414,"text":895},{"id":914,"depth":414,"text":915},{"id":944,"depth":414,"text":945},{"id":976,"depth":414,"text":977},{"id":1054,"depth":414,"text":1055},"How cache-key fragmentation silently lowers CDN hit rates, and the normalisation rules that collapse variants without serving wrong content.","md",{"slug":1113,"type":1114,"breadcrumb":1115,"datePublished":1122,"dateModified":1122},"normalizing-cache-keys-to-raise-hit-rate","article",[1116,1118,1119,1120],{"name":1117,"url":472},"Home",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1121},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate\u002F","2026-10-06",true,"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate",{"title":1126,"description":1127},"Normalizing CDN Cache Keys to Raise Hit Rate","Tracking parameters, parameter order, case and irrelevant headers split one page into many cache entries. Normalise the cache key to raise CDN hit rate and cut TTFB.","advanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fnormalizing-cache-keys-to-raise-hit-rate\u002Findex","gZrel4MaYk75-C64OPb41px482CQWL0PdllUc9tleCc",[1131,1135],{"title":1132,"path":1133,"stem":1134},"Diagnosing CDN Cache Misses from Response Headers","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fdiagnosing-cdn-cache-misses-from-response-headers","advanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fdiagnosing-cdn-cache-misses-from-response-headers\u002Findex",{"title":1136,"path":1137,"stem":1138},"Tiered Caching and Origin Shield","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Ftiered-caching-and-origin-shield","advanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Ftiered-caching-and-origin-shield\u002Findex",1791308079572]