[{"data":1,"prerenderedAt":1764},["ShallowReactive",2],{"page:\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy":3,"all-docs-nav":1519},{"id":4,"title":5,"body":6,"breadcrumb":1494,"dateModified":1504,"datePublished":1504,"description":1505,"extension":1506,"faq":1507,"meta":1513,"navigation":315,"path":1514,"seo":1515,"slug":12,"stem":1516,"type":1517,"__hash__":1518},"content\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy\u002Findex.md","Purging the CDN Cache After a Static Deploy",{"type":7,"value":8,"toc":1474},"minimark",[9,13,17,26,31,44,48,182,186,189,433,436,575,582,586,589,773,827,830,916,920,928,1040,1048,1052,1055,1174,1188,1192,1195,1287,1294,1298,1301,1326,1337,1341,1380,1384,1389,1393,1398,1401,1405,1408,1412,1415,1419,1422,1426,1429,1433,1470],[10,11,5],"h1",{"id":12},"purging-the-cdn-cache-after-a-static-deploy",[14,15,16],"p",{},"The reflex after a deploy is to purge the cache. On a well-configured static site that reflex is usually wrong: hashed assets are immutable and never need invalidating, and HTML is either short-cached or revalidated. A blanket purge throws away every cached object at every edge location and sends the entire next wave of traffic to the origin.",[14,18,19,20,25],{},"This guide covers what genuinely needs purging, how to do it by path or tag rather than wholesale, and how to wire it into a deploy so it runs at the right moment. It is the invalidation half of ",[21,22,24],"a",{"href":23},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002F","CDN Caching Rules for SSGs",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,38,41],"ul",{},[35,36,37],"li",{},"A deploy that produces a list of changed files — most CI systems can diff the build output against the previous artifact.",[35,39,40],{},"API credentials for your CDN with purge permissions, stored as a CI secret.",[35,42,43],{},"A cache policy where assets are content-hashed and immutable; if not, start there.",[27,45,47],{"id":46},"what-actually-needs-purging","What Actually Needs Purging",[49,50,51,178],"figure",{},[52,53,60,61,60,65,60,69,60,76],"svg",{"viewBox":54,"role":55,"ariaLabelledBy":56,"xmlns":59},"0 0 760 300","img",[57,58],"pg-what-title","pg-what-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[62,63,64],"title",{"id":57},"Which files need invalidating after a deploy",[66,67,68],"desc",{"id":58},"Four categories. Hashed assets are immutable and never need purging. HTML with a short TTL expires on its own within a minute. Unhashed assets such as favicon or a logo do need purging when they change. Redirect and header files usually apply on deploy without a purge.",[70,71],"rect",{"x":72,"y":72,"width":73,"height":74,"fill":75},"0","760","300","#ffffff",[77,78,80,81,80,89,80,100,80,106,80,112,80,116,80,122,80,128,80,132,80,136,80,139,80,142,80,149,80,153,80,157,80,161,80,165,80,168,80,171,80,174,60],"g",{"style":79},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[82,83,88],"text",{"x":84,"y":85,"fill":86,"style":87},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Most of a static deploy needs no purge at all",[70,90],{"x":91,"y":92,"width":93,"height":94,"rx":95,"fill":96,"opacity":97,"stroke":98,"style":99},"40","56","330","100","12","#8ac926","0.14","#5a8a16","stroke-width:1.5px",[82,101,105],{"x":102,"y":103,"fill":86,"style":104},"205","82","font-size:13px;font-weight:700;text-anchor:middle","No purge needed",[82,107,111],{"x":108,"y":109,"fill":86,"style":110},"62","110","font-size:12px","hashed CSS and JS — immutable",[82,113,115],{"x":108,"y":114,"fill":86,"style":110},"132","hashed images and fonts",[82,117,121],{"x":108,"y":118,"fill":119,"style":120},"150","#556071","font-size:11px","new filenames, old ones simply age out",[70,123],{"x":124,"y":92,"width":125,"height":94,"rx":95,"fill":126,"opacity":127,"stroke":126,"style":99},"400","320","#1982c4","0.12",[82,129,131],{"x":130,"y":103,"fill":86,"style":104},"560","Expires on its own",[82,133,135],{"x":134,"y":109,"fill":86,"style":110},"424","HTML with max-age=0, revalidated",[82,137,138],{"x":134,"y":114,"fill":86,"style":110},"sitemap.xml, feeds",[82,140,141],{"x":134,"y":118,"fill":119,"style":120},"stale for seconds, not worth a purge",[70,143],{"x":91,"y":144,"width":93,"height":145,"rx":95,"fill":146,"opacity":147,"stroke":148,"style":99},"172","90","#ffca3a","0.26","#a97b00",[82,150,152],{"x":102,"y":151,"fill":86,"style":104},"198","Purge these paths",[82,154,156],{"x":108,"y":155,"fill":86,"style":110},"224","unhashed assets that changed",[82,158,160],{"x":108,"y":159,"fill":86,"style":110},"246","long-cached HTML you edited",[70,162],{"x":124,"y":144,"width":125,"height":145,"rx":95,"fill":163,"opacity":127,"stroke":164,"style":99},"#ff595e","#d83b41",[82,166,167],{"x":130,"y":151,"fill":86,"style":104},"Never purge everything",[82,169,170],{"x":134,"y":155,"fill":86,"style":110},"every edge refetches at once",[82,172,173],{"x":134,"y":159,"fill":86,"style":110},"origin spike right after a change",[82,175,177],{"x":91,"y":176,"fill":119,"style":120},"288","If the top-left box covers most of your output, your cache policy is already doing the work",[179,180,181],"figcaption",{},"The green box is the goal: a build where nothing needs invalidating because nothing was overwritten. Hashed filenames are what make a deploy and a purge independent.",[27,183,185],{"id":184},"purge-by-path-from-the-diff","Purge by Path, From the Diff",[14,187,188],{},"The deploy already knows which files changed. Turn that into a purge list rather than purging by intuition:",[190,191,196],"pre",{"className":192,"code":193,"language":194,"meta":195,"style":195},"language-bash shiki shiki-themes github-light github-dark","#!\u002Fusr\u002Fbin\u002Fenv bash\nset -euo pipefail\n# Compare the new build against the previously deployed artifact\nCHANGED=$(diff -rq prev-dist dist 2>\u002Fdev\u002Fnull \\\n  | awk '\u002F^Files .* differ$\u002F {print $2}; \u002F^Only in dist\u002F {gsub(\u002F:\u002F,\"\",$3); print $3\"\u002F\"$4}' \\\n  | sed 's|^dist||' | grep -vE '\u002F_assets\u002F.*\\.[0-9a-f]{8}\\.' || true)\n\n# Nothing but hashed assets changed → nothing to purge\n[ -z \"$CHANGED\" ] && { echo \"purge: nothing to invalidate\"; exit 0; }\n\necho \"$CHANGED\" | while read -r p; do echo \"https:\u002F\u002Fexample.com$p\"; done > purge-urls.txt\nwc -l \u003C purge-urls.txt\n","bash","",[197,198,199,208,222,228,264,278,310,317,323,362,367,419],"code",{"__ignoreMap":195},[200,201,204],"span",{"class":202,"line":203},"line",1,[200,205,207],{"class":206},"sJ8bj","#!\u002Fusr\u002Fbin\u002Fenv bash\n",[200,209,211,215,218],{"class":202,"line":210},2,[200,212,214],{"class":213},"sj4cs","set",[200,216,217],{"class":213}," -euo",[200,219,221],{"class":220},"sZZnC"," pipefail\n",[200,223,225],{"class":202,"line":224},3,[200,226,227],{"class":206},"# Compare the new build against the previously deployed artifact\n",[200,229,231,235,239,242,246,249,252,255,258,261],{"class":202,"line":230},4,[200,232,234],{"class":233},"sVt8B","CHANGED",[200,236,238],{"class":237},"szBVR","=",[200,240,241],{"class":233},"$(",[200,243,245],{"class":244},"sScJk","diff",[200,247,248],{"class":213}," -rq",[200,250,251],{"class":220}," prev-dist",[200,253,254],{"class":220}," dist",[200,256,257],{"class":237}," 2>",[200,259,260],{"class":220},"\u002Fdev\u002Fnull",[200,262,263],{"class":213}," \\\n",[200,265,267,270,273,276],{"class":202,"line":266},5,[200,268,269],{"class":237},"  |",[200,271,272],{"class":244}," awk",[200,274,275],{"class":220}," '\u002F^Files .* differ$\u002F {print $2}; \u002F^Only in dist\u002F {gsub(\u002F:\u002F,\"\",$3); print $3\"\u002F\"$4}'",[200,277,263],{"class":213},[200,279,281,283,286,289,292,295,298,301,304,307],{"class":202,"line":280},6,[200,282,269],{"class":237},[200,284,285],{"class":244}," sed",[200,287,288],{"class":220}," 's|^dist||'",[200,290,291],{"class":237}," |",[200,293,294],{"class":244}," grep",[200,296,297],{"class":213}," -vE",[200,299,300],{"class":220}," '\u002F_assets\u002F.*\\.[0-9a-f]{8}\\.'",[200,302,303],{"class":237}," ||",[200,305,306],{"class":213}," true",[200,308,309],{"class":233},")\n",[200,311,313],{"class":202,"line":312},7,[200,314,316],{"emptyLinePlaceholder":315},true,"\n",[200,318,320],{"class":202,"line":319},8,[200,321,322],{"class":206},"# Nothing but hashed assets changed → nothing to purge\n",[200,324,326,329,332,335,338,341,344,347,350,353,356,359],{"class":202,"line":325},9,[200,327,328],{"class":233},"[ ",[200,330,331],{"class":237},"-z",[200,333,334],{"class":220}," \"",[200,336,337],{"class":233},"$CHANGED",[200,339,340],{"class":220},"\"",[200,342,343],{"class":233}," ] && { ",[200,345,346],{"class":213},"echo",[200,348,349],{"class":220}," \"purge: nothing to invalidate\"",[200,351,352],{"class":233},"; ",[200,354,355],{"class":213},"exit",[200,357,358],{"class":213}," 0",[200,360,361],{"class":233},"; }\n",[200,363,365],{"class":202,"line":364},10,[200,366,316],{"emptyLinePlaceholder":315},[200,368,370,372,374,376,378,380,383,386,389,392,394,397,400,403,406,408,410,413,416],{"class":202,"line":369},11,[200,371,346],{"class":213},[200,373,334],{"class":220},[200,375,337],{"class":233},[200,377,340],{"class":220},[200,379,291],{"class":237},[200,381,382],{"class":237}," while",[200,384,385],{"class":213}," read",[200,387,388],{"class":213}," -r",[200,390,391],{"class":220}," p",[200,393,352],{"class":233},[200,395,396],{"class":237},"do",[200,398,399],{"class":213}," echo",[200,401,402],{"class":220}," \"https:\u002F\u002Fexample.com",[200,404,405],{"class":233},"$p",[200,407,340],{"class":220},[200,409,352],{"class":233},[200,411,412],{"class":237},"done",[200,414,415],{"class":237}," >",[200,417,418],{"class":233}," purge-urls.txt\n",[200,420,422,425,428,431],{"class":202,"line":421},12,[200,423,424],{"class":244},"wc",[200,426,427],{"class":213}," -l",[200,429,430],{"class":237}," \u003C",[200,432,418],{"class":220},[14,434,435],{},"Then send it in batches — providers cap the number of URLs per call, typically at 30 to 500:",[190,437,439],{"className":192,"code":438,"language":194,"meta":195,"style":195},"split -l 30 purge-urls.txt batch-\nfor f in batch-*; do\n  jq -Rn --args '{files: $ARGS.positional}' $(cat \"$f\") \\\n    | curl -sS -X POST \"https:\u002F\u002Fapi.cloudflare.com\u002Fclient\u002Fv4\u002Fzones\u002F$ZONE\u002Fpurge_cache\" \\\n        -H \"Authorization: Bearer $CF_API_TOKEN\" -H 'Content-Type: application\u002Fjson' --data @-\n  sleep 1\ndone\n",[197,440,441,457,476,509,537,562,570],{"__ignoreMap":195},[200,442,443,446,448,451,454],{"class":202,"line":203},[200,444,445],{"class":244},"split",[200,447,427],{"class":213},[200,449,450],{"class":213}," 30",[200,452,453],{"class":220}," purge-urls.txt",[200,455,456],{"class":220}," batch-\n",[200,458,459,462,465,468,471,473],{"class":202,"line":210},[200,460,461],{"class":237},"for",[200,463,464],{"class":233}," f ",[200,466,467],{"class":237},"in",[200,469,470],{"class":220}," batch-*",[200,472,352],{"class":233},[200,474,475],{"class":237},"do\n",[200,477,478,481,484,487,490,493,496,498,501,503,506],{"class":202,"line":224},[200,479,480],{"class":244},"  jq",[200,482,483],{"class":213}," -Rn",[200,485,486],{"class":213}," --args",[200,488,489],{"class":220}," '{files: $ARGS.positional}'",[200,491,492],{"class":233}," $(",[200,494,495],{"class":244},"cat",[200,497,334],{"class":220},[200,499,500],{"class":233},"$f",[200,502,340],{"class":220},[200,504,505],{"class":233},") ",[200,507,508],{"class":213},"\\\n",[200,510,511,514,517,520,523,526,529,532,535],{"class":202,"line":230},[200,512,513],{"class":237},"    |",[200,515,516],{"class":244}," curl",[200,518,519],{"class":213}," -sS",[200,521,522],{"class":213}," -X",[200,524,525],{"class":220}," POST",[200,527,528],{"class":220}," \"https:\u002F\u002Fapi.cloudflare.com\u002Fclient\u002Fv4\u002Fzones\u002F",[200,530,531],{"class":233},"$ZONE",[200,533,534],{"class":220},"\u002Fpurge_cache\"",[200,536,263],{"class":213},[200,538,539,542,545,548,550,553,556,559],{"class":202,"line":266},[200,540,541],{"class":213},"        -H",[200,543,544],{"class":220}," \"Authorization: Bearer ",[200,546,547],{"class":233},"$CF_API_TOKEN",[200,549,340],{"class":220},[200,551,552],{"class":213}," -H",[200,554,555],{"class":220}," 'Content-Type: application\u002Fjson'",[200,557,558],{"class":213}," --data",[200,560,561],{"class":220}," @-\n",[200,563,564,567],{"class":202,"line":280},[200,565,566],{"class":244},"  sleep",[200,568,569],{"class":213}," 1\n",[200,571,572],{"class":202,"line":312},[200,573,574],{"class":237},"done\n",[14,576,577,578,581],{},"The ",[197,579,580],{},"grep -v"," on hashed paths is the important line: without it, every deploy purges hundreds of asset URLs that did not need it, which is the slow version of purging everything.",[27,583,585],{"id":584},"purge-by-tag-for-template-changes","Purge by Tag for Template Changes",[14,587,588],{},"Path purging breaks down when a template change touches every page in a section. Cache tags solve this: attach a label at the edge, then purge the label.",[190,590,594],{"className":591,"code":592,"language":593,"meta":195,"style":195},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Worker: tag responses by section so a template change purges one group\nexport default {\n  async fetch(request, env, ctx) {\n    const res = await env.ASSETS.fetch(request);\n    const section = new URL(request.url).pathname.split('\u002F')[1] || 'home';\n    const out = new Response(res.body, res);\n    out.headers.set('cache-tag', `section-${section},site`);\n    return out;\n  },\n};\n","javascript",[197,595,596,601,612,641,669,712,729,755,763,768],{"__ignoreMap":195},[200,597,598],{"class":202,"line":203},[200,599,600],{"class":206},"\u002F\u002F Worker: tag responses by section so a template change purges one group\n",[200,602,603,606,609],{"class":202,"line":210},[200,604,605],{"class":237},"export",[200,607,608],{"class":237}," default",[200,610,611],{"class":233}," {\n",[200,613,614,617,620,623,627,630,633,635,638],{"class":202,"line":224},[200,615,616],{"class":237},"  async",[200,618,619],{"class":244}," fetch",[200,621,622],{"class":233},"(",[200,624,626],{"class":625},"s4XuR","request",[200,628,629],{"class":233},", ",[200,631,632],{"class":625},"env",[200,634,629],{"class":233},[200,636,637],{"class":625},"ctx",[200,639,640],{"class":233},") {\n",[200,642,643,646,649,652,655,658,661,663,666],{"class":202,"line":230},[200,644,645],{"class":237},"    const",[200,647,648],{"class":213}," res",[200,650,651],{"class":237}," =",[200,653,654],{"class":237}," await",[200,656,657],{"class":233}," env.",[200,659,660],{"class":213},"ASSETS",[200,662,25],{"class":233},[200,664,665],{"class":244},"fetch",[200,667,668],{"class":233},"(request);\n",[200,670,671,673,676,678,681,684,687,689,691,694,697,700,703,706,709],{"class":202,"line":266},[200,672,645],{"class":237},[200,674,675],{"class":213}," section",[200,677,651],{"class":237},[200,679,680],{"class":237}," new",[200,682,683],{"class":244}," URL",[200,685,686],{"class":233},"(request.url).pathname.",[200,688,445],{"class":244},[200,690,622],{"class":233},[200,692,693],{"class":220},"'\u002F'",[200,695,696],{"class":233},")[",[200,698,699],{"class":213},"1",[200,701,702],{"class":233},"] ",[200,704,705],{"class":237},"||",[200,707,708],{"class":220}," 'home'",[200,710,711],{"class":233},";\n",[200,713,714,716,719,721,723,726],{"class":202,"line":280},[200,715,645],{"class":237},[200,717,718],{"class":213}," out",[200,720,651],{"class":237},[200,722,680],{"class":237},[200,724,725],{"class":244}," Response",[200,727,728],{"class":233},"(res.body, res);\n",[200,730,731,734,736,738,741,743,746,749,752],{"class":202,"line":312},[200,732,733],{"class":233},"    out.headers.",[200,735,214],{"class":244},[200,737,622],{"class":233},[200,739,740],{"class":220},"'cache-tag'",[200,742,629],{"class":233},[200,744,745],{"class":220},"`section-${",[200,747,748],{"class":233},"section",[200,750,751],{"class":220},"},site`",[200,753,754],{"class":233},");\n",[200,756,757,760],{"class":202,"line":319},[200,758,759],{"class":237},"    return",[200,761,762],{"class":233}," out;\n",[200,764,765],{"class":202,"line":325},[200,766,767],{"class":233},"  },\n",[200,769,770],{"class":202,"line":364},[200,771,772],{"class":233},"};\n",[190,774,776],{"className":192,"code":775,"language":194,"meta":195,"style":195},"# One call invalidates every page in the guides section\ncurl -sS -X POST \"https:\u002F\u002Fapi.cloudflare.com\u002Fclient\u002Fv4\u002Fzones\u002F$ZONE\u002Fpurge_cache\" \\\n  -H \"Authorization: Bearer $CF_API_TOKEN\" -H 'Content-Type: application\u002Fjson' \\\n  --data '{\"tags\":[\"section-guides\"]}'\n",[197,777,778,783,802,819],{"__ignoreMap":195},[200,779,780],{"class":202,"line":203},[200,781,782],{"class":206},"# One call invalidates every page in the guides section\n",[200,784,785,788,790,792,794,796,798,800],{"class":202,"line":210},[200,786,787],{"class":244},"curl",[200,789,519],{"class":213},[200,791,522],{"class":213},[200,793,525],{"class":220},[200,795,528],{"class":220},[200,797,531],{"class":233},[200,799,534],{"class":220},[200,801,263],{"class":213},[200,803,804,807,809,811,813,815,817],{"class":202,"line":224},[200,805,806],{"class":213},"  -H",[200,808,544],{"class":220},[200,810,547],{"class":233},[200,812,340],{"class":220},[200,814,552],{"class":213},[200,816,555],{"class":220},[200,818,263],{"class":213},[200,820,821,824],{"class":202,"line":230},[200,822,823],{"class":213},"  --data",[200,825,826],{"class":220}," '{\"tags\":[\"section-guides\"]}'\n",[14,828,829],{},"Tagging is worth the setup on any site where template changes are routine. It turns \"purge 1,200 URLs in 40 batched calls\" into one call, and it makes the intent legible in the deploy log.",[49,831,832,913],{},[52,833,60,838,60,841,60,844,60,847],{"viewBox":834,"role":55,"ariaLabelledBy":835,"xmlns":59},"0 0 760 290",[836,837],"pg-strategy-title","pg-strategy-desc",[62,839,840],{"id":836},"Origin load after three purge strategies",[66,842,843],{"id":837},"Three lines showing origin requests per second in the two minutes after a deploy. Purging everything spikes to 1,850 requests per second. Purging by tag for one section peaks at 240. Purging only changed paths peaks at 60, barely above the baseline of 35.",[70,845],{"x":72,"y":72,"width":73,"height":846,"fill":75},"290",[77,848,80,849,80,853,80,857,80,860,80,864,80,868,80,874,80,877,80,881,80,886,80,891,80,896,80,902,80,905,80,909,60],{"style":79},[82,850,852],{"x":84,"y":851,"fill":86,"style":87},"26","Origin requests per second after the deploy",[202,854],{"x1":94,"y1":855,"x2":94,"y2":856,"stroke":119,"style":99},"52","212",[202,858],{"x1":94,"y1":856,"x2":859,"y2":856,"stroke":119,"style":99},"700",[82,861,863],{"x":91,"y":862,"fill":119,"style":120},"58","2,000",[82,865,72],{"x":866,"y":867,"fill":119,"style":120},"72","216",[869,870],"polyline",{"points":871,"fill":872,"stroke":164,"style":873},"140,205 200,64 300,120 420,180 620,203","none","stroke-width:2.5px",[869,875],{"points":876,"fill":872,"stroke":148,"style":873},"140,206 200,190 300,196 420,204 620,206",[869,878],{"points":879,"fill":872,"stroke":98,"style":880},"140,208 200,207 300,208 420,208 620,208","stroke-width:2.5px;stroke-dasharray:7 5",[82,882,885],{"x":883,"y":862,"fill":164,"style":884},"210","font-size:11px;font-weight:700","purge everything · 1,850\u002Fs",[82,887,890],{"x":888,"y":889,"fill":148,"style":884},"310","188","purge by tag · 240\u002Fs",[82,892,895],{"x":893,"y":894,"fill":98,"style":884},"440","200","purge changed paths · 60\u002Fs",[82,897,901],{"x":898,"y":899,"fill":119,"style":900},"140","234","font-size:11px;text-anchor:middle","deploy",[82,903,904],{"x":74,"y":899,"fill":119,"style":900},"+30 s",[82,906,908],{"x":907,"y":899,"fill":119,"style":900},"620","+120 s",[82,910,912],{"x":94,"y":911,"fill":119,"style":120},"266","Same site, same traffic, same deploy · baseline origin load is 35 requests per second",[179,914,915],{},"The red line is not a graph of cache invalidation; it is a graph of a site attacking its own origin. Everything it refetched was already correct.",[27,917,919],{"id":918},"run-it-after-the-deploy-never-before","Run It After the Deploy, Never Before",[14,921,922,923,927],{},"Ordering is the failure that produces the most confusing symptoms. Purge before the new build is live and the edge refetches the ",[924,925,926],"em",{},"old"," content and caches it again — now with a fresh TTL. The site is stale for longer than if you had done nothing.",[190,929,933],{"className":930,"code":931,"language":932,"meta":195,"style":195},"language-yaml shiki shiki-themes github-light github-dark","# .github\u002Fworkflows\u002Fdeploy.yml (excerpt)\n- name: Deploy\n  run: npx wrangler deploy\n\n- name: Verify the new build is live\n  run: |\n    for i in $(seq 1 20); do\n      LIVE=$(curl -s https:\u002F\u002Fexample.com\u002F | grep -o 'content=\"[a-f0-9]\\{7\\}\"' | head -1)\n      [ \"$LIVE\" = \"content=\\\"${GITHUB_SHA:0:7}\\\"\" ] && break\n      sleep 3\n    done\n\n- name: Purge changed paths\n  run: .\u002Fscripts\u002Fpurge.sh\n","yaml",[197,934,935,940,955,965,969,980,989,994,999,1004,1009,1014,1018,1030],{"__ignoreMap":195},[200,936,937],{"class":202,"line":203},[200,938,939],{"class":206},"# .github\u002Fworkflows\u002Fdeploy.yml (excerpt)\n",[200,941,942,945,949,952],{"class":202,"line":210},[200,943,944],{"class":233},"- ",[200,946,948],{"class":947},"s9eBZ","name",[200,950,951],{"class":233},": ",[200,953,954],{"class":220},"Deploy\n",[200,956,957,960,962],{"class":202,"line":224},[200,958,959],{"class":947},"  run",[200,961,951],{"class":233},[200,963,964],{"class":220},"npx wrangler deploy\n",[200,966,967],{"class":202,"line":230},[200,968,316],{"emptyLinePlaceholder":315},[200,970,971,973,975,977],{"class":202,"line":266},[200,972,944],{"class":233},[200,974,948],{"class":947},[200,976,951],{"class":233},[200,978,979],{"class":220},"Verify the new build is live\n",[200,981,982,984,986],{"class":202,"line":280},[200,983,959],{"class":947},[200,985,951],{"class":233},[200,987,988],{"class":237},"|\n",[200,990,991],{"class":202,"line":312},[200,992,993],{"class":220},"    for i in $(seq 1 20); do\n",[200,995,996],{"class":202,"line":319},[200,997,998],{"class":220},"      LIVE=$(curl -s https:\u002F\u002Fexample.com\u002F | grep -o 'content=\"[a-f0-9]\\{7\\}\"' | head -1)\n",[200,1000,1001],{"class":202,"line":325},[200,1002,1003],{"class":220},"      [ \"$LIVE\" = \"content=\\\"${GITHUB_SHA:0:7}\\\"\" ] && break\n",[200,1005,1006],{"class":202,"line":364},[200,1007,1008],{"class":220},"      sleep 3\n",[200,1010,1011],{"class":202,"line":369},[200,1012,1013],{"class":220},"    done\n",[200,1015,1016],{"class":202,"line":421},[200,1017,316],{"emptyLinePlaceholder":315},[200,1019,1021,1023,1025,1027],{"class":202,"line":1020},13,[200,1022,944],{"class":233},[200,1024,948],{"class":947},[200,1026,951],{"class":233},[200,1028,1029],{"class":220},"Purge changed paths\n",[200,1031,1033,1035,1037],{"class":202,"line":1032},14,[200,1034,959],{"class":947},[200,1036,951],{"class":233},[200,1038,1039],{"class":220},".\u002Fscripts\u002Fpurge.sh\n",[14,1041,1042,1043,1047],{},"The verification step is what makes the ordering reliable rather than hopeful: it checks the build stamp described in ",[21,1044,1046],{"href":1045},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002F","Rollbacks and Deploy Safety for Static Sites"," before invalidating anything.",[27,1049,1051],{"id":1050},"soft-purge-where-it-is-available","Soft Purge Where It Is Available",[14,1053,1054],{},"Several providers offer a gentler operation than eviction: mark an object stale rather than deleting it. The edge then serves the stale copy while it revalidates in the background, so readers never wait for the origin and the origin never sees a stampede.",[49,1056,1057,1171],{},[52,1058,60,1063,60,1066,60,1069,60,1072,60,1157],{"viewBox":1059,"role":55,"ariaLabelledBy":1060,"xmlns":59},"0 0 760 280",[1061,1062],"pg-soft-title","pg-soft-desc",[62,1064,1065],{"id":1061},"Hard purge versus soft purge at the edge",[66,1067,1068],{"id":1062},"With a hard purge the object is deleted, so the next reader waits for a full origin fetch. With a soft purge the object is marked stale, so the next reader is served the stale copy immediately while the edge revalidates in the background and the following reader gets fresh content.",[70,1070],{"x":72,"y":72,"width":73,"height":1071,"fill":75},"280",[77,1073,80,1074,80,1077,80,1081,80,1087,80,1093,80,1097,80,1101,80,1105,80,1109,80,1113,80,1117,80,1121,80,1123,80,1127,80,1131,80,1133,80,1135,80,1153,60],{"style":79},[82,1075,1076],{"x":84,"y":851,"fill":86,"style":87},"Who waits for the origin",[82,1078,1080],{"x":91,"y":866,"fill":164,"style":1079},"font-size:12px;font-weight:700","Hard purge",[70,1082],{"x":1083,"y":92,"width":1084,"height":1085,"rx":1086,"fill":163,"opacity":97,"stroke":164,"style":99},"160","170","44","8",[82,1088,1092],{"x":1089,"y":1090,"fill":86,"style":1091},"245","83","font-size:12px;text-anchor:middle","object deleted",[70,1094],{"x":1095,"y":92,"width":1096,"height":1085,"rx":1086,"fill":163,"opacity":127,"stroke":164,"style":99},"350","190",[82,1098,1100],{"x":1099,"y":1090,"fill":86,"style":1091},"445","next reader waits ~180 ms",[70,1102],{"x":130,"y":92,"width":1083,"height":1085,"rx":1086,"fill":1103,"opacity":1104,"stroke":119,"style":99},"#d9e2ef","0.5",[82,1106,1108],{"x":1107,"y":1090,"fill":86,"style":1091},"640","fresh, cached again",[82,1110,1112],{"x":91,"y":1111,"fill":98,"style":1079},"182","Soft purge",[70,1114],{"x":1083,"y":1115,"width":1084,"height":1085,"rx":1086,"fill":96,"opacity":1116,"stroke":98,"style":99},"166","0.18",[82,1118,1120],{"x":1089,"y":1119,"fill":86,"style":1091},"193","object marked stale",[70,1122],{"x":1095,"y":1115,"width":1096,"height":1085,"rx":1086,"fill":96,"opacity":97,"stroke":98,"style":99},[82,1124,1126],{"x":1099,"y":1125,"fill":86,"style":1091},"187","next reader served instantly",[82,1128,1130],{"x":1099,"y":1129,"fill":119,"style":900},"203","revalidation happens behind them",[70,1132],{"x":130,"y":1115,"width":1083,"height":1085,"rx":1086,"fill":1103,"opacity":1104,"stroke":119,"style":99},[82,1134,1108],{"x":1107,"y":1119,"fill":86,"style":1091},[77,1136,1138,1139,1138,1144,1138,1147,1138,1150,80],{"stroke":119,"fill":872,"style":1137},"stroke-width:2px","\n      ",[1140,1141],"path",{"d":1142,"style":1143},"M332 78 L348 78","marker-end:url(#pg-soft-arrow)",[1140,1145],{"d":1146,"style":1143},"M542 78 L558 78",[1140,1148],{"d":1149,"style":1143},"M332 188 L348 188",[1140,1151],{"d":1152,"style":1143},"M542 188 L558 188",[82,1154,1156],{"x":91,"y":1155,"fill":119,"style":120},"256","The same effect is available without a purge API by serving stale-while-revalidate in Cache-Control",[1158,1159,80,1160,60],"defs",{},[1161,1162,1138,1168,80],"marker",{"id":1163,"viewBox":1164,"refX":1086,"refY":1165,"markerWidth":1166,"markerHeight":1166,"orient":1167},"pg-soft-arrow","0 0 10 10","5","7","auto-start-reverse",[1140,1169],{"d":1170,"fill":119},"M0 0 L10 5 L0 10 z",[179,1172,1173],{},"A soft purge costs one reader a slightly stale page and saves every reader the origin round trip. On content sites that trade is almost always worth taking.",[14,1175,1176,1177,1180,1181,1184,1185,1187],{},"If your provider has no soft-purge API, ",[197,1178,1179],{},"stale-while-revalidate"," in the response headers gets you most of the same behaviour without any purge call at all — the edge serves the cached copy and refreshes it in the background once it passes its freshness window. Combining a short ",[197,1182,1183],{},"max-age"," with a generous ",[197,1186,1179],{}," means routine deploys need no invalidation step whatsoever, which is the least error-prone configuration available.",[27,1189,1191],{"id":1190},"measured-impact","Measured Impact",[14,1193,1194],{},"A documentation site with 1,600 pages, 380 assets and roughly 40 requests per second of steady traffic, measured across ten deploys of each strategy:",[1196,1197,1198,1220],"table",{},[1199,1200,1201],"thead",{},[1202,1203,1204,1208,1211,1214,1217],"tr",{},[1205,1206,1207],"th",{},"Strategy",[1205,1209,1210],{},"Purge calls",[1205,1212,1213],{},"Origin peak",[1205,1215,1216],{},"Time to steady state",[1205,1218,1219],{},"Stale window",[1221,1222,1223,1240,1255,1271],"tbody",{},[1202,1224,1225,1229,1231,1234,1237],{},[1226,1227,1228],"td",{},"Purge everything",[1226,1230,699],{},[1226,1232,1233],{},"1,850 req\u002Fs",[1226,1235,1236],{},"2 m 10 s",[1226,1238,1239],{},"0 s",[1202,1241,1242,1245,1247,1250,1253],{},[1226,1243,1244],{},"Purge by tag (one section)",[1226,1246,699],{},[1226,1248,1249],{},"240 req\u002Fs",[1226,1251,1252],{},"35 s",[1226,1254,1239],{},[1202,1256,1257,1260,1263,1266,1269],{},[1226,1258,1259],{},"Purge changed paths only",[1226,1261,1262],{},"3",[1226,1264,1265],{},"60 req\u002Fs",[1226,1267,1268],{},"8 s",[1226,1270,1239],{},[1202,1272,1273,1276,1278,1281,1284],{},[1226,1274,1275],{},"No purge (hashed assets, short-TTL HTML)",[1226,1277,72],{},[1226,1279,1280],{},"41 req\u002Fs",[1226,1282,1283],{},"—",[1226,1285,1286],{},"up to 60 s",[14,1288,1289,1290,1293],{},"The last row deserves consideration rather than dismissal. On a site whose HTML carries ",[197,1291,1292],{},"max-age=0, must-revalidate",", skipping the purge entirely costs a revalidation round trip per page and no staleness at all — the edge asks the origin whether its copy is current, and gets a cheap 304 when it is. That is a perfectly good default, and it is why the invalidation strategy is really a consequence of the caching policy rather than a separate decision.",[27,1295,1297],{"id":1296},"verify-rather-than-assume","Verify Rather Than Assume",[14,1299,1300],{},"A purge API returning success means the request was accepted, not that every edge location has acted on it. Confirm by fetching through the edge and reading the cache status header:",[190,1302,1304],{"className":192,"code":1303,"language":194,"meta":195,"style":195},"curl -sI https:\u002F\u002Fexample.com\u002Fguides\u002Fdeploying\u002F | grep -iE 'cf-cache-status|age|x-cache'\n",[197,1305,1306],{"__ignoreMap":195},[200,1307,1308,1310,1313,1316,1318,1320,1323],{"class":202,"line":203},[200,1309,787],{"class":244},[200,1311,1312],{"class":213}," -sI",[200,1314,1315],{"class":220}," https:\u002F\u002Fexample.com\u002Fguides\u002Fdeploying\u002F",[200,1317,291],{"class":237},[200,1319,294],{"class":244},[200,1321,1322],{"class":213}," -iE",[200,1324,1325],{"class":220}," 'cf-cache-status|age|x-cache'\n",[14,1327,1328,1329,1332,1333,1336],{},"An ",[197,1330,1331],{},"age"," value that resets to zero and a ",[197,1334,1335],{},"MISS"," on the first request after a purge is what completion looks like. Checking two or three representative paths from different regions — a CI runner and a laptop are usually enough — catches the case where a purge succeeded in one region and is still propagating in another.",[27,1338,1340],{"id":1339},"pitfalls-rollback","Pitfalls & Rollback",[32,1342,1343,1350,1356,1362,1368,1374],{},[35,1344,1345,1349],{},[1346,1347,1348],"strong",{},"Purging everything by default."," It is the option most likely to cause an incident and least likely to be necessary.",[35,1351,1352,1355],{},[1346,1353,1354],{},"Purging before the deploy completes."," The edge re-caches the old build with a fresh TTL.",[35,1357,1358,1361],{},[1346,1359,1360],{},"Purging hashed assets."," They are immutable; the URL changed, so nothing is stale.",[35,1363,1364,1367],{},[1346,1365,1366],{},"Assuming the API response means propagation."," Purges are eventually consistent; verify by fetching, not by trusting a 200.",[35,1369,1370,1373],{},[1346,1371,1372],{},"Unbatched purge lists."," Providers cap URLs per call and rate-limit; a loop of single-URL calls will be throttled halfway through.",[35,1375,1376,1379],{},[1346,1377,1378],{},"Rollback:"," purging is not reversible, but it is not destructive either — the worst case is a period of higher origin load. If a purge script misbehaves, disable the CI step; the caching policy alone keeps the site correct.",[27,1381,1383],{"id":1382},"conclusion","Conclusion",[14,1385,1386,1387,25],{},"On a static site with hashed assets and short-TTL HTML, the correct purge after most deploys is no purge at all. When invalidation is genuinely needed, derive the list from the build diff, exclude hashed paths, batch the calls, and run it only after verifying the new build is live. Reach for cache tags when template changes routinely touch whole sections. The caching policy that makes all of this cheap is in ",[21,1388,24],{"href":23},[27,1390,1392],{"id":1391},"faq","FAQ",[1394,1395,1397],"h3",{"id":1396},"do-i-need-to-purge-at-all-if-my-assets-are-hashed","Do I need to purge at all if my assets are hashed?",[14,1399,1400],{},"Rarely. Hashed assets are immutable, so nothing to invalidate, and HTML is normally served with a short TTL or revalidation. A purge is only needed for unhashed files that changed, or when HTML is cached long at the edge.",[1394,1402,1404],{"id":1403},"why-is-purging-everything-harmful","Why is purging everything harmful?",[14,1406,1407],{},"Because every edge location loses every object at once and refetches from the origin on the next request. On a busy site that is a self-inflicted traffic spike at exactly the moment you have just changed something, which is also the worst moment to be debugging origin load.",[1394,1409,1411],{"id":1410},"what-is-cache-tagging-and-when-should-i-use-it","What is cache tagging and when should I use it?",[14,1413,1414],{},"Tagging attaches labels to responses at the edge so you can purge a group in one call — for example every page in a section. It is worth setting up on sites where a template change affects a known subset of pages, because it turns thousands of path purges into one tag purge.",[1394,1416,1418],{"id":1417},"how-long-does-a-purge-take-to-propagate","How long does a purge take to propagate?",[14,1420,1421],{},"Seconds to a couple of minutes depending on the provider and the number of paths. Treat it as eventually consistent, and never assume a purge has completed just because the API returned success.",[1394,1423,1425],{"id":1424},"should-the-purge-run-before-or-after-the-deploy","Should the purge run before or after the deploy?",[14,1427,1428],{},"After, always. Purging before the new content is live invites the edge to refetch and re-cache the old version, which leaves you worse off than not purging at all.",[27,1430,1432],{"id":1431},"related","Related",[32,1434,1435,1444,1451,1458,1463],{},[35,1436,1437,1440,1441,1443],{},[1346,1438,1439],{},"Parent:"," ",[21,1442,24],{"href":23}," — the policy that decides whether you need this at all.",[35,1445,1446,1450],{},[21,1447,1449],{"href":1448},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages\u002F","Setting Cache-Control Headers on Cloudflare Pages"," — the headers this depends on.",[35,1452,1453,1457],{},[21,1454,1456],{"href":1455},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify\u002F","Setting Up Proper Cache Headers on Netlify"," — the same policy on another host.",[35,1459,1460,1462],{},[21,1461,1046],{"href":1045}," — the build stamp the purge step verifies.",[35,1464,1465,1469],{},[21,1466,1468],{"href":1467},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads\u002F","Atomic Deploys vs Incremental Uploads"," — why nothing is overwritten in the first place.",[1471,1472,1473],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":195,"searchDepth":210,"depth":210,"links":1475},[1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1493],{"id":29,"depth":210,"text":30},{"id":46,"depth":210,"text":47},{"id":184,"depth":210,"text":185},{"id":584,"depth":210,"text":585},{"id":918,"depth":210,"text":919},{"id":1050,"depth":210,"text":1051},{"id":1190,"depth":210,"text":1191},{"id":1296,"depth":210,"text":1297},{"id":1339,"depth":210,"text":1340},{"id":1382,"depth":210,"text":1383},{"id":1391,"depth":210,"text":1392,"children":1487},[1488,1489,1490,1491,1492],{"id":1396,"depth":224,"text":1397},{"id":1403,"depth":224,"text":1404},{"id":1410,"depth":224,"text":1411},{"id":1417,"depth":224,"text":1418},{"id":1424,"depth":224,"text":1425},{"id":1431,"depth":210,"text":1432},[1495,1498,1501,1502],{"name":1496,"item":1497},"Home","\u002F",{"name":1499,"item":1500},"Performance Optimization & Core Web Vitals for SSGs","\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002F",{"name":24,"item":23},{"name":5,"item":1503},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy\u002F","2026-08-01","Purge by path or tag instead of everything: why a full purge is an origin stampede, which files actually need invalidating, and how to wire a surgical purge into CI.","md",[1508,1509,1510,1511,1512],{"q":1397,"a":1400},{"q":1404,"a":1407},{"q":1411,"a":1414},{"q":1418,"a":1421},{"q":1425,"a":1428},{},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy",{"title":5,"description":1505},"performance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy\u002Findex","article","vD5WJ6Ls4Q3F5y_i6ySMGNU5-wB0DszzCedUlGpzJKg",[1520,1523,1526,1529,1532,1535,1538,1541,1544,1547,1550,1553,1556,1559,1562,1565,1568,1571,1574,1577,1580,1583,1586,1589,1592,1595,1598,1600,1601,1604,1607,1610,1613,1616,1619,1622,1625,1628,1631,1634,1637,1640,1643,1646,1649,1652,1655,1658,1661,1664,1667,1670,1673,1676,1679,1682,1685,1688,1691,1694,1697,1700,1703,1706,1709,1712,1715,1718,1721,1724,1727,1730,1733,1736,1739,1742,1745,1748,1751,1754,1756,1758,1761],{"path":1521,"title":1522},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1524,"title":1525},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1527,"title":1528},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1530,"title":1531},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1533,"title":1534},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1536,"title":1537},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1539,"title":1540},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1542,"title":1543},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1545,"title":1546},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs","Eleventy vs Jekyll for Markdown-Heavy Blogs",{"path":1548,"title":1549},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1551,"title":1552},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy","Replacing Jekyll Plugins When Migrating to Eleventy",{"path":1554,"title":1555},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins","Running Jekyll on GitHub Pages Without Plugins",{"path":1557,"title":1558},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1560,"title":1561},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration","Keeping Redirects Working After an SSG Migration",{"path":1563,"title":1564},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo","Migrating a Docs Site From Jekyll to Hugo",{"path":1566,"title":1567},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1569,"title":1570},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators","Porting Shortcodes and Includes Between Generators",{"path":1572,"title":1573},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1575,"title":1576},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1578,"title":1579},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1581,"title":1582},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1584,"title":1585},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1587,"title":1588},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1590,"title":1591},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1593,"title":1594},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1596,"title":1597},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1599,"title":24},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs",{"path":1514,"title":5},{"path":1602,"title":1603},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1605,"title":1606},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1608,"title":1609},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts","Eliminating Layout Shift From Web Fonts",{"path":1611,"title":1612},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1614,"title":1615},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1617,"title":1618},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js","Measuring CLS in the Field With web-vitals.js",{"path":1620,"title":1621},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift","Reserving Space for Images and Embeds",{"path":1623,"title":1624},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1626,"title":1627},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":1629,"title":1630},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render","Subsetting Variable Fonts for Faster First Render",{"path":1632,"title":1633},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":1635,"title":1636},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1638,"title":1639},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":1641,"title":1642},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":1644,"title":1645},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1647,"title":1648},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1650,"title":1651},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":1653,"title":1654},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1656,"title":1657},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":1659,"title":1660},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":1662,"title":1663},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1665,"title":1666},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":1668,"title":1669},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":1671,"title":1672},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":1674,"title":1675},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1677,"title":1678},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":1680,"title":1681},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":1683,"title":1684},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1686,"title":1687},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":1689,"title":1690},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1692,"title":1693},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":1695,"title":1696},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1698,"title":1699},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds","Scheduling Content Publication With Cron-Triggered Builds",{"path":1701,"title":1702},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build","Wiring a Headless CMS to a Static Build",{"path":1704,"title":1705},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":1707,"title":1708},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":1710,"title":1711},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":1713,"title":1714},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1716,"title":1717},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":1719,"title":1720},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1722,"title":1723},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1725,"title":1726},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":1728,"title":1729},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":1731,"title":1732},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1734,"title":1735},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1737,"title":1738},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1740,"title":1741},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":1743,"title":1744},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":1746,"title":1747},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":1749,"title":1750},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1752,"title":1753},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1755,"title":1468},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads",{"path":1757,"title":1046},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites",{"path":1759,"title":1760},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":1762,"title":1763},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",1785611671074]