[{"data":1,"prerenderedAt":2072},["ShallowReactive",2],{"page:\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites":3,"all-docs-nav":1828},{"id":4,"title":5,"body":6,"breadcrumb":1802,"dateModified":1811,"datePublished":1811,"description":1812,"extension":1813,"faq":1814,"meta":1822,"navigation":530,"path":1823,"seo":1824,"slug":12,"stem":1825,"type":1826,"__hash__":1827},"content\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites\u002Findex.md","Serving AVIF With Fallbacks on Static Sites",{"type":7,"value":8,"toc":1784},"minimark",[9,13,17,35,40,61,65,80,257,281,435,439,1092,1103,1111,1115,1118,1121,1249,1256,1260,1273,1284,1361,1364,1445,1449,1452,1526,1533,1609,1613,1668,1672,1680,1684,1689,1692,1696,1699,1703,1712,1716,1731,1735,1738,1742,1780],[10,11,5],"h1",{"id":12},"serving-avif-with-fallbacks-on-static-sites",[14,15,16],"p",{},"AVIF is the most efficient widely supported image format, typically 20-30% smaller than WebP at matched quality and dramatically smaller than the JPEG most sites still ship. On a static site the entire cost is paid at build time, so readers get the smaller file and the pipeline absorbs the encoding.",[14,18,19,20,24,25,28,29,34],{},"The complication is that \"widely supported\" is not \"universally supported\", and the fallback mechanics are easy to get subtly wrong — a source order that defeats the point, a ",[21,22,23],"code",{},"Vary"," header that poisons a cache, a ",[21,26,27],{},"\u003Cpicture>"," element that loses the dimensions that keep layout stable. This guide gets all of it right. It sits under ",[30,31,33],"a",{"href":32},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002F","Image Optimization Pipelines in Astro",".",[36,37,39],"h2",{"id":38},"prerequisites","Prerequisites",[41,42,43,55,58],"ul",{},[44,45,46,47,50,51,54],"li",{},"A build-time image pipeline: ",[21,48,49],{},"astro:assets",", ",[21,52,53],{},"@11ty\u002Feleventy-img",", Hugo image resources, or Sharp called directly.",[44,56,57],{},"Source images at least as large as the biggest rendered size you need.",[44,59,60],{},"A way to cache build output, since AVIF encoding is the slowest step in most image pipelines.",[36,62,64],{"id":63},"order-the-sources-correctly","Order the Sources Correctly",[14,66,67,68,70,71,75,76,79],{},"The ",[21,69,27],{}," element picks the ",[72,73,74],"strong",{},"first"," source whose ",[21,77,78],{},"type"," the browser supports. That makes ordering a correctness issue rather than a style one:",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-html shiki shiki-themes github-light github-dark","\u003Cpicture>\n  \u003Csource type=\"image\u002Favif\" srcset=\"\u002Fimg\u002Fhero-480.avif 480w, \u002Fimg\u002Fhero-960.avif 960w, \u002Fimg\u002Fhero-1600.avif 1600w\"\n          sizes=\"(min-width: 768px) 720px, 100vw\">\n  \u003Csource type=\"image\u002Fwebp\" srcset=\"\u002Fimg\u002Fhero-480.webp 480w, \u002Fimg\u002Fhero-960.webp 960w, \u002Fimg\u002Fhero-1600.webp 1600w\"\n          sizes=\"(min-width: 768px) 720px, 100vw\">\n  \u003Cimg src=\"\u002Fimg\u002Fhero-960.jpg\" width=\"1600\" height=\"900\" alt=\"Deployment dashboard\"\n       loading=\"eager\" fetchpriority=\"high\" decoding=\"async\">\n\u003C\u002Fpicture>\n","html","",[21,88,89,105,133,146,167,178,218,247],{"__ignoreMap":86},[90,91,94,98,102],"span",{"class":92,"line":93},"line",1,[90,95,97],{"class":96},"sVt8B","\u003C",[90,99,101],{"class":100},"s9eBZ","picture",[90,103,104],{"class":96},">\n",[90,106,108,111,114,118,121,125,128,130],{"class":92,"line":107},2,[90,109,110],{"class":96},"  \u003C",[90,112,113],{"class":100},"source",[90,115,117],{"class":116},"sScJk"," type",[90,119,120],{"class":96},"=",[90,122,124],{"class":123},"sZZnC","\"image\u002Favif\"",[90,126,127],{"class":116}," srcset",[90,129,120],{"class":96},[90,131,132],{"class":123},"\"\u002Fimg\u002Fhero-480.avif 480w, \u002Fimg\u002Fhero-960.avif 960w, \u002Fimg\u002Fhero-1600.avif 1600w\"\n",[90,134,136,139,141,144],{"class":92,"line":135},3,[90,137,138],{"class":116},"          sizes",[90,140,120],{"class":96},[90,142,143],{"class":123},"\"(min-width: 768px) 720px, 100vw\"",[90,145,104],{"class":96},[90,147,149,151,153,155,157,160,162,164],{"class":92,"line":148},4,[90,150,110],{"class":96},[90,152,113],{"class":100},[90,154,117],{"class":116},[90,156,120],{"class":96},[90,158,159],{"class":123},"\"image\u002Fwebp\"",[90,161,127],{"class":116},[90,163,120],{"class":96},[90,165,166],{"class":123},"\"\u002Fimg\u002Fhero-480.webp 480w, \u002Fimg\u002Fhero-960.webp 960w, \u002Fimg\u002Fhero-1600.webp 1600w\"\n",[90,168,170,172,174,176],{"class":92,"line":169},5,[90,171,138],{"class":116},[90,173,120],{"class":96},[90,175,143],{"class":123},[90,177,104],{"class":96},[90,179,181,183,186,189,191,194,197,199,202,205,207,210,213,215],{"class":92,"line":180},6,[90,182,110],{"class":96},[90,184,185],{"class":100},"img",[90,187,188],{"class":116}," src",[90,190,120],{"class":96},[90,192,193],{"class":123},"\"\u002Fimg\u002Fhero-960.jpg\"",[90,195,196],{"class":116}," width",[90,198,120],{"class":96},[90,200,201],{"class":123},"\"1600\"",[90,203,204],{"class":116}," height",[90,206,120],{"class":96},[90,208,209],{"class":123},"\"900\"",[90,211,212],{"class":116}," alt",[90,214,120],{"class":96},[90,216,217],{"class":123},"\"Deployment dashboard\"\n",[90,219,221,224,226,229,232,234,237,240,242,245],{"class":92,"line":220},7,[90,222,223],{"class":116},"       loading",[90,225,120],{"class":96},[90,227,228],{"class":123},"\"eager\"",[90,230,231],{"class":116}," fetchpriority",[90,233,120],{"class":96},[90,235,236],{"class":123},"\"high\"",[90,238,239],{"class":116}," decoding",[90,241,120],{"class":96},[90,243,244],{"class":123},"\"async\"",[90,246,104],{"class":96},[90,248,250,253,255],{"class":92,"line":249},8,[90,251,252],{"class":96},"\u003C\u002F",[90,254,101],{"class":100},[90,256,104],{"class":96},[14,258,259,260,263,264,267,268,271,272,276,277,280],{},"Three details are load-bearing. AVIF comes first because it is the most efficient. The ",[21,261,262],{},"\u003Cimg>"," carries ",[21,265,266],{},"width"," and ",[21,269,270],{},"height",", which is what reserves layout space — see ",[30,273,275],{"href":274},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift\u002F","Reserving Space for Images and Embeds",". And ",[21,278,279],{},"sizes"," is repeated on every source, because each one is evaluated independently.",[282,283,284,431],"figure",{},[285,286,292,293,292,297,292,301,292,308,292,416],"svg",{"viewBox":287,"role":185,"ariaLabelledBy":288,"xmlns":291},"0 0 760 300",[289,290],"av-order-title","av-order-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[294,295,296],"title",{"id":289},"How a browser resolves a picture element",[298,299,300],"desc",{"id":290},"A decision flow. The browser reads sources in order: if it supports AVIF it takes the first source and stops. If not, it evaluates the WebP source and takes that if supported. If neither is supported it falls through to the img element's JPEG. A note shows that listing WebP first would give AVIF-capable browsers the larger file.",[302,303],"rect",{"x":304,"y":304,"width":305,"height":306,"fill":307},"0","760","300","#ffffff",[309,310,312,313,312,321,312,332,312,338,312,343,312,348,312,352,312,356,312,361,312,365,312,368,312,381,312,387,312,390,312,399,312,404,312,408,312,412,292],"g",{"style":311},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[314,315,320],"text",{"x":316,"y":317,"fill":318,"style":319},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","First supported source wins — order is the algorithm",[302,322],{"x":323,"y":324,"width":325,"height":326,"rx":327,"fill":328,"opacity":329,"stroke":330,"style":331},"40","60","170","52","10","#8ac926","0.18","#5a8a16","stroke-width:1.5px",[314,333,337],{"x":334,"y":335,"fill":318,"style":336},"125","84","font-size:12px;font-weight:700;text-anchor:middle","source: AVIF",[314,339,342],{"x":334,"y":340,"fill":330,"style":341},"102","font-size:11px;text-anchor:middle","92% of traffic stops here",[302,344],{"x":345,"y":324,"width":325,"height":326,"rx":327,"fill":346,"opacity":347,"stroke":346,"style":331},"250","#1982c4","0.14",[314,349,351],{"x":350,"y":335,"fill":318,"style":336},"335","source: WebP",[314,353,355],{"x":350,"y":340,"fill":354,"style":341},"#556071","7% stop here",[302,357],{"x":358,"y":324,"width":325,"height":326,"rx":327,"fill":359,"opacity":360,"stroke":354,"style":331},"460","#d9e2ef","0.55",[314,362,364],{"x":363,"y":335,"fill":318,"style":336},"545","img: JPEG",[314,366,367],{"x":363,"y":340,"fill":354,"style":341},"\u003C1% fall through",[309,369,372,373,372,378,312],{"stroke":354,"fill":370,"style":371},"none","stroke-width:2px","\n      ",[374,375],"path",{"d":376,"style":377},"M212 86 L248 86","marker-end:url(#av-arrow)",[374,379],{"d":380,"style":377},"M422 86 L458 86",[314,382,386],{"x":383,"y":384,"fill":354,"style":385},"222","132","font-size:11px","not supported",[314,388,386],{"x":389,"y":384,"fill":354,"style":385},"432",[302,391],{"x":323,"y":392,"width":393,"height":394,"rx":395,"fill":396,"opacity":397,"stroke":398,"style":331},"172","680","86","12","#ff595e","0.10","#d83b41",[314,400,403],{"x":316,"y":401,"fill":318,"style":402},"198","font-size:13px;font-weight:700;text-anchor:middle","The common mistake",[314,405,407],{"x":316,"y":383,"fill":318,"style":406},"font-size:12px;text-anchor:middle","WebP listed first → every AVIF-capable browser takes WebP and stops",[314,409,411],{"x":316,"y":410,"fill":354,"style":406},"244","The AVIF files are still generated, still deployed, and never served",[314,413,415],{"x":323,"y":414,"fill":354,"style":385},"288","Support shares from a documentation site's own analytics, mid-2026 — check your own before assuming",[417,418,312,419,292],"defs",{},[420,421,372,428,312],"marker",{"id":422,"viewBox":423,"refX":424,"refY":425,"markerWidth":426,"markerHeight":426,"orient":427},"av-arrow","0 0 10 10","8","5","7","auto-start-reverse",[374,429],{"d":430,"fill":354},"M0 0 L10 5 L0 10 z",[432,433,434],"figcaption",{},"Nothing errors when the order is wrong — the pipeline still produces AVIF, the site still works, and the bytes you paid encoding time for are never delivered.",[36,436,438],{"id":437},"generate-the-variants-in-one-step","Generate the Variants in One Step",[81,440,444],{"className":441,"code":442,"language":443,"meta":86,"style":86},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F scripts\u002Fencode-images.mjs — one source in, nine files out, cached by content hash\nimport { createHash } from 'node:crypto';\nimport { mkdir, readFile, writeFile, access } from 'node:fs\u002Fpromises';\nimport { globSync } from 'node:fs';\nimport path from 'node:path';\nimport sharp from 'sharp';\n\nconst WIDTHS = [480, 960, 1600];\nconst OUT = 'dist\u002Fimg';\nawait mkdir(OUT, { recursive: true });\n\nfor (const src of globSync('src\u002Fimages\u002F**\u002F*.{jpg,jpeg,png}')) {\n  const buf = await readFile(src);\n  const hash = createHash('sha1').update(buf).digest('hex').slice(0, 8);\n  const base = path.basename(src).replace(\u002F\\.[^.]+$\u002F, '');\n\n  for (const w of WIDTHS) {\n    const resized = sharp(buf).resize(w, null, { withoutEnlargement: true });\n    const jobs = [\n      [`${base}-${w}.${hash}.avif`, resized.clone().avif({ quality: 55, effort: 4 })],\n      [`${base}-${w}.${hash}.webp`, resized.clone().webp({ quality: 76 })],\n      [`${base}-${w}.${hash}.jpg`,  resized.clone().jpeg({ quality: 78, mozjpeg: true })],\n    ];\n    for (const [name, pipeline] of jobs) {\n      const out = path.join(OUT, name);\n      try { await access(out); continue; } catch { \u002F* not cached *\u002F }\n      await writeFile(out, await pipeline.toBuffer());\n    }\n  }\n}\n","javascript",[21,445,446,452,470,484,498,512,526,532,563,578,602,607,634,654,705,757,762,782,814,827,881,917,959,965,994,1017,1051,1074,1080,1086],{"__ignoreMap":86},[90,447,448],{"class":92,"line":93},[90,449,451],{"class":450},"sJ8bj","\u002F\u002F scripts\u002Fencode-images.mjs — one source in, nine files out, cached by content hash\n",[90,453,454,458,461,464,467],{"class":92,"line":107},[90,455,457],{"class":456},"szBVR","import",[90,459,460],{"class":96}," { createHash } ",[90,462,463],{"class":456},"from",[90,465,466],{"class":123}," 'node:crypto'",[90,468,469],{"class":96},";\n",[90,471,472,474,477,479,482],{"class":92,"line":135},[90,473,457],{"class":456},[90,475,476],{"class":96}," { mkdir, readFile, writeFile, access } ",[90,478,463],{"class":456},[90,480,481],{"class":123}," 'node:fs\u002Fpromises'",[90,483,469],{"class":96},[90,485,486,488,491,493,496],{"class":92,"line":148},[90,487,457],{"class":456},[90,489,490],{"class":96}," { globSync } ",[90,492,463],{"class":456},[90,494,495],{"class":123}," 'node:fs'",[90,497,469],{"class":96},[90,499,500,502,505,507,510],{"class":92,"line":169},[90,501,457],{"class":456},[90,503,504],{"class":96}," path ",[90,506,463],{"class":456},[90,508,509],{"class":123}," 'node:path'",[90,511,469],{"class":96},[90,513,514,516,519,521,524],{"class":92,"line":180},[90,515,457],{"class":456},[90,517,518],{"class":96}," sharp ",[90,520,463],{"class":456},[90,522,523],{"class":123}," 'sharp'",[90,525,469],{"class":96},[90,527,528],{"class":92,"line":220},[90,529,531],{"emptyLinePlaceholder":530},true,"\n",[90,533,534,537,541,544,547,550,552,555,557,560],{"class":92,"line":249},[90,535,536],{"class":456},"const",[90,538,540],{"class":539},"sj4cs"," WIDTHS",[90,542,543],{"class":456}," =",[90,545,546],{"class":96}," [",[90,548,549],{"class":539},"480",[90,551,50],{"class":96},[90,553,554],{"class":539},"960",[90,556,50],{"class":96},[90,558,559],{"class":539},"1600",[90,561,562],{"class":96},"];\n",[90,564,566,568,571,573,576],{"class":92,"line":565},9,[90,567,536],{"class":456},[90,569,570],{"class":539}," OUT",[90,572,543],{"class":456},[90,574,575],{"class":123}," 'dist\u002Fimg'",[90,577,469],{"class":96},[90,579,581,584,587,590,593,596,599],{"class":92,"line":580},10,[90,582,583],{"class":456},"await",[90,585,586],{"class":116}," mkdir",[90,588,589],{"class":96},"(",[90,591,592],{"class":539},"OUT",[90,594,595],{"class":96},", { recursive: ",[90,597,598],{"class":539},"true",[90,600,601],{"class":96}," });\n",[90,603,605],{"class":92,"line":604},11,[90,606,531],{"emptyLinePlaceholder":530},[90,608,610,613,616,618,620,623,626,628,631],{"class":92,"line":609},12,[90,611,612],{"class":456},"for",[90,614,615],{"class":96}," (",[90,617,536],{"class":456},[90,619,188],{"class":539},[90,621,622],{"class":456}," of",[90,624,625],{"class":116}," globSync",[90,627,589],{"class":96},[90,629,630],{"class":123},"'src\u002Fimages\u002F**\u002F*.{jpg,jpeg,png}'",[90,632,633],{"class":96},")) {\n",[90,635,637,640,643,645,648,651],{"class":92,"line":636},13,[90,638,639],{"class":456},"  const",[90,641,642],{"class":539}," buf",[90,644,543],{"class":456},[90,646,647],{"class":456}," await",[90,649,650],{"class":116}," readFile",[90,652,653],{"class":96},"(src);\n",[90,655,657,659,662,664,667,669,672,675,678,681,684,686,689,691,694,696,698,700,702],{"class":92,"line":656},14,[90,658,639],{"class":456},[90,660,661],{"class":539}," hash",[90,663,543],{"class":456},[90,665,666],{"class":116}," createHash",[90,668,589],{"class":96},[90,670,671],{"class":123},"'sha1'",[90,673,674],{"class":96},").",[90,676,677],{"class":116},"update",[90,679,680],{"class":96},"(buf).",[90,682,683],{"class":116},"digest",[90,685,589],{"class":96},[90,687,688],{"class":123},"'hex'",[90,690,674],{"class":96},[90,692,693],{"class":116},"slice",[90,695,589],{"class":96},[90,697,304],{"class":539},[90,699,50],{"class":96},[90,701,424],{"class":539},[90,703,704],{"class":96},");\n",[90,706,708,710,713,715,718,721,724,727,729,732,736,739,742,745,748,750,752,755],{"class":92,"line":707},15,[90,709,639],{"class":456},[90,711,712],{"class":539}," base",[90,714,543],{"class":456},[90,716,717],{"class":96}," path.",[90,719,720],{"class":116},"basename",[90,722,723],{"class":96},"(src).",[90,725,726],{"class":116},"replace",[90,728,589],{"class":96},[90,730,731],{"class":123},"\u002F",[90,733,735],{"class":734},"snhLl","\\.",[90,737,738],{"class":539},"[",[90,740,741],{"class":456},"^",[90,743,744],{"class":539},".]",[90,746,747],{"class":456},"+$",[90,749,731],{"class":123},[90,751,50],{"class":96},[90,753,754],{"class":123},"''",[90,756,704],{"class":96},[90,758,760],{"class":92,"line":759},16,[90,761,531],{"emptyLinePlaceholder":530},[90,763,765,768,770,772,775,777,779],{"class":92,"line":764},17,[90,766,767],{"class":456},"  for",[90,769,615],{"class":96},[90,771,536],{"class":456},[90,773,774],{"class":539}," w",[90,776,622],{"class":456},[90,778,540],{"class":539},[90,780,781],{"class":96},") {\n",[90,783,785,788,791,793,796,798,801,804,807,810,812],{"class":92,"line":784},18,[90,786,787],{"class":456},"    const",[90,789,790],{"class":539}," resized",[90,792,543],{"class":456},[90,794,795],{"class":116}," sharp",[90,797,680],{"class":96},[90,799,800],{"class":116},"resize",[90,802,803],{"class":96},"(w, ",[90,805,806],{"class":539},"null",[90,808,809],{"class":96},", { withoutEnlargement: ",[90,811,598],{"class":539},[90,813,601],{"class":96},[90,815,817,819,822,824],{"class":92,"line":816},19,[90,818,787],{"class":456},[90,820,821],{"class":539}," jobs",[90,823,543],{"class":456},[90,825,826],{"class":96}," [\n",[90,828,830,833,836,839,842,845,848,851,854,857,860,863,866,869,872,875,878],{"class":92,"line":829},20,[90,831,832],{"class":96},"      [",[90,834,835],{"class":123},"`${",[90,837,838],{"class":96},"base",[90,840,841],{"class":123},"}-${",[90,843,844],{"class":96},"w",[90,846,847],{"class":123},"}.${",[90,849,850],{"class":96},"hash",[90,852,853],{"class":123},"}.avif`",[90,855,856],{"class":96},", resized.",[90,858,859],{"class":116},"clone",[90,861,862],{"class":96},"().",[90,864,865],{"class":116},"avif",[90,867,868],{"class":96},"({ quality: ",[90,870,871],{"class":539},"55",[90,873,874],{"class":96},", effort: ",[90,876,877],{"class":539},"4",[90,879,880],{"class":96}," })],\n",[90,882,884,886,888,890,892,894,896,898,901,903,905,907,910,912,915],{"class":92,"line":883},21,[90,885,832],{"class":96},[90,887,835],{"class":123},[90,889,838],{"class":96},[90,891,841],{"class":123},[90,893,844],{"class":96},[90,895,847],{"class":123},[90,897,850],{"class":96},[90,899,900],{"class":123},"}.webp`",[90,902,856],{"class":96},[90,904,859],{"class":116},[90,906,862],{"class":96},[90,908,909],{"class":116},"webp",[90,911,868],{"class":96},[90,913,914],{"class":539},"76",[90,916,880],{"class":96},[90,918,920,922,924,926,928,930,932,934,937,940,942,944,947,949,952,955,957],{"class":92,"line":919},22,[90,921,832],{"class":96},[90,923,835],{"class":123},[90,925,838],{"class":96},[90,927,841],{"class":123},[90,929,844],{"class":96},[90,931,847],{"class":123},[90,933,850],{"class":96},[90,935,936],{"class":123},"}.jpg`",[90,938,939],{"class":96},",  resized.",[90,941,859],{"class":116},[90,943,862],{"class":96},[90,945,946],{"class":116},"jpeg",[90,948,868],{"class":96},[90,950,951],{"class":539},"78",[90,953,954],{"class":96},", mozjpeg: ",[90,956,598],{"class":539},[90,958,880],{"class":96},[90,960,962],{"class":92,"line":961},23,[90,963,964],{"class":96},"    ];\n",[90,966,968,971,973,975,977,980,982,985,988,991],{"class":92,"line":967},24,[90,969,970],{"class":456},"    for",[90,972,615],{"class":96},[90,974,536],{"class":456},[90,976,546],{"class":96},[90,978,979],{"class":539},"name",[90,981,50],{"class":96},[90,983,984],{"class":539},"pipeline",[90,986,987],{"class":96},"] ",[90,989,990],{"class":456},"of",[90,992,993],{"class":96}," jobs) {\n",[90,995,997,1000,1003,1005,1007,1010,1012,1014],{"class":92,"line":996},25,[90,998,999],{"class":456},"      const",[90,1001,1002],{"class":539}," out",[90,1004,543],{"class":456},[90,1006,717],{"class":96},[90,1008,1009],{"class":116},"join",[90,1011,589],{"class":96},[90,1013,592],{"class":539},[90,1015,1016],{"class":96},", name);\n",[90,1018,1020,1023,1026,1028,1031,1034,1037,1040,1043,1045,1048],{"class":92,"line":1019},26,[90,1021,1022],{"class":456},"      try",[90,1024,1025],{"class":96}," { ",[90,1027,583],{"class":456},[90,1029,1030],{"class":116}," access",[90,1032,1033],{"class":96},"(out); ",[90,1035,1036],{"class":456},"continue",[90,1038,1039],{"class":96},"; } ",[90,1041,1042],{"class":456},"catch",[90,1044,1025],{"class":96},[90,1046,1047],{"class":450},"\u002F* not cached *\u002F",[90,1049,1050],{"class":96}," }\n",[90,1052,1054,1057,1060,1063,1065,1068,1071],{"class":92,"line":1053},27,[90,1055,1056],{"class":456},"      await",[90,1058,1059],{"class":116}," writeFile",[90,1061,1062],{"class":96},"(out, ",[90,1064,583],{"class":456},[90,1066,1067],{"class":96}," pipeline.",[90,1069,1070],{"class":116},"toBuffer",[90,1072,1073],{"class":96},"());\n",[90,1075,1077],{"class":92,"line":1076},28,[90,1078,1079],{"class":96},"    }\n",[90,1081,1083],{"class":92,"line":1082},29,[90,1084,1085],{"class":96},"  }\n",[90,1087,1089],{"class":92,"line":1088},30,[90,1090,1091],{"class":96},"}\n",[14,1093,1094,1095,1098,1099,1102],{},"Two choices deserve explanation. ",[72,1096,1097],{},"AVIF quality 55 against WebP 76"," is not a typo: the two scales are not comparable, and AVIF holds up at a much lower number for the same perceived quality. And ",[21,1100,1101],{},"effort: 4"," rather than the maximum trades a few per cent of file size for roughly half the encoding time, which matters when a build encodes hundreds of images.",[14,1104,1105,1106,1110],{},"Content-hashing the filename is what makes the output immutably cacheable, per ",[30,1107,1109],{"href":1108},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002F","CDN Caching Rules for SSGs",", and it doubles as the cache key for the encoder — an unchanged source produces the same filename, which already exists, so nothing is re-encoded.",[36,1112,1114],{"id":1113},"do-not-assume-avif-always-wins","Do Not Assume AVIF Always Wins",[14,1116,1117],{},"For photographic content it nearly always does. For flat illustrations, diagrams and screenshots containing text, WebP or even a well-optimised PNG can be smaller, and AVIF's lossy encoding smears thin high-contrast edges in a way that is very visible on a screenshot of a terminal.",[14,1119,1120],{},"Pick per image rather than by policy:",[81,1122,1124],{"className":441,"code":1123,"language":443,"meta":86,"style":86},"\u002F\u002F After encoding, keep only the smaller of avif\u002Fwebp for this image\nconst [avif, webp] = await Promise.all([stat(avifPath), stat(webpPath)]);\nif (avif.size >= webp.size * 0.95) {\n  await rm(avifPath);            \u002F\u002F AVIF is not winning enough to justify the source\n  console.log(`${base}: keeping webp (${webp.size} \u003C ${avif.size})`);\n}\n",[21,1125,1126,1131,1171,1193,1207,1245],{"__ignoreMap":86},[90,1127,1128],{"class":92,"line":93},[90,1129,1130],{"class":450},"\u002F\u002F After encoding, keep only the smaller of avif\u002Fwebp for this image\n",[90,1132,1133,1135,1137,1139,1141,1143,1145,1147,1149,1152,1154,1157,1160,1163,1166,1168],{"class":92,"line":107},[90,1134,536],{"class":456},[90,1136,546],{"class":96},[90,1138,865],{"class":539},[90,1140,50],{"class":96},[90,1142,909],{"class":539},[90,1144,987],{"class":96},[90,1146,120],{"class":456},[90,1148,647],{"class":456},[90,1150,1151],{"class":539}," Promise",[90,1153,34],{"class":96},[90,1155,1156],{"class":116},"all",[90,1158,1159],{"class":96},"([",[90,1161,1162],{"class":116},"stat",[90,1164,1165],{"class":96},"(avifPath), ",[90,1167,1162],{"class":116},[90,1169,1170],{"class":96},"(webpPath)]);\n",[90,1172,1173,1176,1179,1182,1185,1188,1191],{"class":92,"line":135},[90,1174,1175],{"class":456},"if",[90,1177,1178],{"class":96}," (avif.size ",[90,1180,1181],{"class":456},">=",[90,1183,1184],{"class":96}," webp.size ",[90,1186,1187],{"class":456},"*",[90,1189,1190],{"class":539}," 0.95",[90,1192,781],{"class":96},[90,1194,1195,1198,1201,1204],{"class":92,"line":148},[90,1196,1197],{"class":456},"  await",[90,1199,1200],{"class":116}," rm",[90,1202,1203],{"class":96},"(avifPath);            ",[90,1205,1206],{"class":450},"\u002F\u002F AVIF is not winning enough to justify the source\n",[90,1208,1209,1212,1215,1217,1219,1221,1224,1226,1228,1231,1234,1236,1238,1240,1243],{"class":92,"line":169},[90,1210,1211],{"class":96},"  console.",[90,1213,1214],{"class":116},"log",[90,1216,589],{"class":96},[90,1218,835],{"class":123},[90,1220,838],{"class":96},[90,1222,1223],{"class":123},"}: keeping webp (${",[90,1225,909],{"class":96},[90,1227,34],{"class":123},[90,1229,1230],{"class":96},"size",[90,1232,1233],{"class":123},"} \u003C ${",[90,1235,865],{"class":96},[90,1237,34],{"class":123},[90,1239,1230],{"class":96},[90,1241,1242],{"class":123},"})`",[90,1244,704],{"class":96},[90,1246,1247],{"class":92,"line":180},[90,1248,1091],{"class":96},[14,1250,1251,1252,1255],{},"Then omit the AVIF ",[21,1253,1254],{},"\u003Csource>"," for that image. A build-time decision keeps the markup honest and prevents the odd result where the \"most efficient\" format is the largest file on the page.",[36,1257,1259],{"id":1258},"vary-headers-and-the-negotiation-trap","Vary Headers and the Negotiation Trap",[14,1261,1262,1263,1265,1266,1268,1269,1272],{},"With ",[21,1264,27],{},", each variant has its own URL and the browser chooses, so no negotiation happens at the server and no ",[21,1267,23],{}," header is needed. The trap appears when something in front of the site rewrites a single URL based on ",[21,1270,1271],{},"Accept"," — an image CDN, a Worker, a host feature.",[14,1274,1275,1276,1279,1280,1283],{},"In that case the response ",[72,1277,1278],{},"must"," carry ",[21,1281,1282],{},"Vary: Accept",", or a cache will serve the AVIF it stored for a modern browser to a client that cannot decode it. The symptom is a broken image for a small share of readers, and it is invisible in testing because your own browser gets a correct variant.",[1285,1286,1287,1308],"table",{},[1288,1289,1290],"thead",{},[1291,1292,1293,1297,1300,1305],"tr",{},[1294,1295,1296],"th",{},"Setup",[1294,1298,1299],{},"Who chooses",[1294,1301,1302,1304],{},[21,1303,23],{}," needed",[1294,1306,1307],{},"Cache risk",[1309,1310,1311,1328,1346],"tbody",{},[1291,1312,1313,1319,1322,1325],{},[1314,1315,1316,1318],"td",{},[21,1317,27],{}," with per-format URLs",[1314,1320,1321],{},"Browser",[1314,1323,1324],{},"No",[1314,1326,1327],{},"None",[1291,1329,1330,1335,1338,1343],{},[1314,1331,1332,1333],{},"Single URL, CDN negotiates on ",[21,1334,1271],{},[1314,1336,1337],{},"Edge",[1314,1339,1340],{},[72,1341,1342],{},"Yes",[1314,1344,1345],{},"Broken images if omitted",[1291,1347,1348,1351,1354,1358],{},[1314,1349,1350],{},"Single URL, Worker rewrites",[1314,1352,1353],{},"Worker",[1314,1355,1356],{},[72,1357,1342],{},[1314,1359,1360],{},"Same, plus lower hit ratio",[14,1362,1363],{},"The per-URL approach is simpler and caches better — every variant is a separate immutable object with a 100% hit ratio, rather than one URL whose cache is fragmented by header value.",[282,1365,1366,1442],{},[285,1367,292,1372,292,1375,292,1378,292,1381],{"viewBox":1368,"role":185,"ariaLabelledBy":1369,"xmlns":291},"0 0 760 292",[1370,1371],"av-vary-title","av-vary-desc",[294,1373,1374],{"id":1370},"Per-URL variants versus Accept negotiation",[298,1376,1377],{"id":1371},"With per-URL variants each format is a separate immutable cache object and the browser picks, giving a full cache hit ratio. With Accept-based negotiation one URL maps to several bodies, requiring a Vary header, and the cache is fragmented by Accept value with a lower hit ratio.",[302,1379],{"x":304,"y":304,"width":305,"height":1380,"fill":307},"292",[309,1382,312,1383,312,1385,312,1389,312,1393,312,1397,312,1401,312,1405,312,1409,312,1412,312,1418,312,1422,312,1426,312,1429,312,1432,312,1435,312,1438,292],{"style":311},[314,1384,1374],{"x":316,"y":317,"fill":318,"style":319},[302,1386],{"x":323,"y":326,"width":1387,"height":1388,"rx":395,"fill":328,"opacity":347,"stroke":330,"style":331},"325","192",[314,1390,1392],{"x":1391,"y":951,"fill":318,"style":402},"202","Per-URL variants",[314,1394,1396],{"x":324,"y":1395,"fill":318,"style":385},"106","hero.a91f3c2.avif",[314,1398,1400],{"x":324,"y":1399,"fill":318,"style":385},"130","hero.a91f3c2.webp",[314,1402,1404],{"x":324,"y":1403,"fill":318,"style":385},"154","hero.a91f3c2.jpg",[314,1406,1408],{"x":324,"y":1407,"fill":318,"style":385},"178","no Vary · immutable · 100% hit ratio",[314,1410,1411],{"x":324,"y":1391,"fill":318,"style":385},"browser decides from the picture element",[302,1413],{"x":1414,"y":326,"width":1387,"height":1388,"rx":395,"fill":1415,"opacity":1416,"stroke":1417,"style":331},"395","#ffca3a","0.22","#a97b00",[314,1419,1421],{"x":1420,"y":951,"fill":318,"style":402},"557","Accept negotiation",[314,1423,1425],{"x":1424,"y":1395,"fill":318,"style":385},"415","\u002Fimg\u002Fhero.jpg → 3 possible bodies",[314,1427,1428],{"x":1424,"y":1399,"fill":318,"style":385},"Vary: Accept required",[314,1430,1431],{"x":1424,"y":1403,"fill":318,"style":385},"cache split by Accept value",[314,1433,1434],{"x":1424,"y":1407,"fill":318,"style":385},"lower hit ratio · silent breakage if Vary is lost",[314,1436,1437],{"x":1424,"y":1391,"fill":318,"style":385},"edge decides, and must be told to",[314,1439,1441],{"x":323,"y":1440,"fill":354,"style":385},"280","On a static site the left column is almost always the right answer — the build already produced every variant",[432,1443,1444],{},"Negotiation exists for systems that cannot enumerate variants ahead of time. A static build can, which removes both the header requirement and the cache fragmentation.",[36,1446,1448],{"id":1447},"measured-impact","Measured Impact",[14,1450,1451],{},"An article template with one hero and five body images, Chrome 4× CPU throttle on a fast 3G profile, median of five runs:",[1285,1453,1454,1470],{},[1288,1455,1456],{},[1291,1457,1458,1461,1464,1467],{},[1294,1459,1460],{},"Format policy",[1294,1462,1463],{},"Image transfer",[1294,1465,1466],{},"LCP",[1294,1468,1469],{},"Build time (6 images × 3 widths)",[1309,1471,1472,1486,1500,1514],{},[1291,1473,1474,1477,1480,1483],{},[1314,1475,1476],{},"JPEG only, q78",[1314,1478,1479],{},"812 KB",[1314,1481,1482],{},"3.10 s",[1314,1484,1485],{},"1.4 s",[1291,1487,1488,1491,1494,1497],{},[1314,1489,1490],{},"WebP with JPEG fallback",[1314,1492,1493],{},"386 KB",[1314,1495,1496],{},"2.18 s",[1314,1498,1499],{},"3.9 s",[1291,1501,1502,1505,1508,1511],{},[1314,1503,1504],{},"AVIF + WebP + JPEG",[1314,1506,1507],{},"268 KB",[1314,1509,1510],{},"1.94 s",[1314,1512,1513],{},"22.6 s",[1291,1515,1516,1519,1521,1523],{},[1314,1517,1518],{},"AVIF + WebP + JPEG, cached build",[1314,1520,1507],{},[1314,1522,1510],{},[1314,1524,1525],{},"0.3 s",[14,1527,1528,1529,34],{},"The build-time row is the honest cost: AVIF encoding is roughly six times slower than WebP, which turns a two-second image step into twenty. Caching by content hash removes it entirely for unchanged images, so only a genuinely new image pays — the same caching discipline described in ",[30,1530,1532],{"href":1531},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds\u002F","Caching node_modules in GitHub Actions for Faster SSG Builds",[282,1534,1535,1606],{},[285,1536,292,1541,292,1544,292,1547,292,1549],{"viewBox":1537,"role":185,"ariaLabelledBy":1538,"xmlns":291},"0 0 760 280",[1539,1540],"av-bytes-title","av-bytes-desc",[294,1542,1543],{"id":1539},"Image transfer and build time by format policy",[298,1545,1546],{"id":1540},"A paired comparison. JPEG only transfers 812 kilobytes and builds in 1.4 seconds. WebP with a JPEG fallback transfers 386 kilobytes and builds in 3.9 seconds. AVIF, WebP and JPEG transfers 268 kilobytes but builds in 22.6 seconds, dropping to 0.3 seconds when the encoder output is cached.",[302,1548],{"x":304,"y":304,"width":305,"height":1440,"fill":307},[309,1550,312,1551,312,1555,312,1560,312,1564,312,1568,312,1572,312,1576,312,1580,312,1584,312,1586,312,1591,312,1595,312,1599,312,1603,292],{"style":311},[314,1552,1554],{"x":316,"y":1553,"fill":318,"style":319},"26","Readers pay bytes, the pipeline pays seconds",[92,1556],{"x1":1557,"y1":1558,"x2":1557,"y2":1559,"stroke":359,"style":331},"260","44","200",[314,1561,1563],{"x":324,"y":951,"fill":318,"style":1562},"font-size:12px;font-weight:700","JPEG only",[302,1565],{"x":1557,"y":324,"width":1566,"height":317,"rx":425,"fill":396,"opacity":1567,"stroke":398,"style":331},"420","0.16",[314,1569,1479],{"x":1570,"y":1571,"fill":398,"style":336},"470","79",[314,1573,1575],{"x":324,"y":1574,"fill":318,"style":1562},"128","WebP + JPEG",[302,1577],{"x":1557,"y":1578,"width":1559,"height":317,"rx":425,"fill":1415,"opacity":1579,"stroke":1417,"style":331},"110","0.34",[314,1581,1493],{"x":1582,"y":1583,"fill":318,"style":336},"360","129",[314,1585,1504],{"x":324,"y":1407,"fill":318,"style":1562},[302,1587],{"x":1557,"y":1588,"width":1589,"height":317,"rx":425,"fill":328,"opacity":1590,"stroke":330,"style":331},"160","139","0.26",[314,1592,1507],{"x":1593,"y":1594,"fill":318,"style":336},"330","179",[314,1596,1598],{"x":1597,"y":1594,"fill":330,"style":1562},"416","LCP 1.94 s",[314,1600,1602],{"x":324,"y":1601,"fill":354,"style":385},"226","Build time: 1.4 s → 3.9 s → 22.6 s uncached, and 0.3 s once encoder output is cached by content hash",[314,1604,1605],{"x":324,"y":345,"fill":354,"style":385},"Shared linear scale on transfer · 6 images × 3 widths, Chrome 4× CPU throttle, fast 3G",[432,1607,1608],{},"The encoding cost is real and entirely absorbed by the cache. What reaches readers is a third of the original transfer and a 1.2-second LCP improvement.",[36,1610,1612],{"id":1611},"pitfalls-rollback","Pitfalls & Rollback",[41,1614,1615,1621,1634,1642,1648,1659],{},[44,1616,1617,1620],{},[72,1618,1619],{},"WebP listed before AVIF."," Everything works and the AVIF is never served. Order most-efficient first.",[44,1622,1623,1628,1629,1631,1632,34],{},[72,1624,1625,1627],{},[21,1626,279],{}," on only one source."," Each ",[21,1630,1254],{}," is evaluated independently and needs its own ",[21,1633,279],{},[44,1635,1636,1641],{},[72,1637,1638,1639,34],{},"Dimensions missing from the ",[21,1640,262],{}," The fallback element is what reserves layout space; without it the picture still shifts the page.",[44,1643,1644,1647],{},[72,1645,1646],{},"Assuming AVIF wins for screenshots."," Compare bytes per image and drop the AVIF source when it does not.",[44,1649,1650,1658],{},[72,1651,1652,1653,1655,1656,34],{},"Negotiating on ",[21,1654,1271],{}," without ",[21,1657,23],{}," A cache will hand an AVIF to a client that cannot decode it.",[44,1660,1661,1664,1665,1667],{},[72,1662,1663],{},"Rollback:"," remove the AVIF ",[21,1666,1254],{}," line from the template. The WebP and JPEG variants are already deployed, so the page keeps working with no rebuild and no cache purge.",[36,1669,1671],{"id":1670},"conclusion","Conclusion",[14,1673,1674,1675,1677,1678,34],{},"Serving AVIF properly is three decisions: order sources by efficiency, keep dimensions on the fallback ",[21,1676,262],{},", and let the browser choose from per-format URLs rather than negotiating at the edge. Encode with format-appropriate quality settings, cache by content hash so the slow encoder runs only for new images, and check per image that AVIF is actually smaller before shipping the source. The wider pipeline is in ",[30,1679,33],{"href":32},[36,1681,1683],{"id":1682},"faq","FAQ",[1685,1686,1688],"h3",{"id":1687},"is-avif-always-smaller-than-webp","Is AVIF always smaller than WebP?",[14,1690,1691],{},"Usually, by 20 to 30 percent at matched visual quality on photographic content, and occasionally not — flat illustrations and screenshots with sharp text sometimes encode smaller as WebP or even as optimised PNG. Encode both, compare the actual bytes, and keep whichever wins per image.",[1685,1693,1695],{"id":1694},"does-avif-cost-more-to-decode-on-low-end-devices","Does AVIF cost more to decode on low-end devices?",[14,1697,1698],{},"It decodes more slowly than WebP, by a few milliseconds for typical content sizes, which is far outweighed by transferring 30 percent fewer bytes on a slow connection. On a fast connection with a very cheap device the trade narrows but still favours the smaller file.",[1685,1700,1702],{"id":1701},"why-does-picture-source-order-matter","Why does picture source order matter?",[14,1704,1705,1706,1708,1709,1711],{},"The browser takes the first source whose ",[21,1707,78],{}," it supports, so listing WebP before AVIF means AVIF-capable browsers still get WebP. Order sources from most to least efficient, with the plain ",[21,1710,262],{}," last as the universal fallback.",[1685,1713,1715],{"id":1714},"do-i-need-vary-headers-for-content-negotiation","Do I need Vary headers for content negotiation?",[14,1717,1718,1719,1721,1722,1724,1725,1727,1728,1730],{},"Not with the ",[21,1720,27],{}," element, because the browser chooses and each variant has its own URL. ",[21,1723,23],{}," matters only when a server or CDN rewrites a single URL based on the ",[21,1726,1271],{}," header, which is where mis-set ",[21,1729,23],{}," silently poisons caches.",[1685,1732,1734],{"id":1733},"how-much-does-encoding-avif-slow-the-build","How much does encoding AVIF slow the build?",[14,1736,1737],{},"Substantially — AVIF encoding is several times slower than WebP. Cache the encoded output keyed on the source file so only new or changed images pay the cost, and consider a lower encoder effort setting for large batches.",[36,1739,1741],{"id":1740},"related","Related",[41,1743,1744,1753,1760,1768,1775],{},[44,1745,1746,1749,1750,1752],{},[72,1747,1748],{},"Parent:"," ",[30,1751,33],{"href":32}," — the pipeline that generates these variants.",[44,1754,1755,1759],{},[30,1756,1758],{"href":1757},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins\u002F","Optimizing WebP Images in Hugo Without Plugins"," — the same work in Hugo's native pipeline.",[44,1761,1762,1764,1765,1767],{},[30,1763,275],{"href":274}," — the dimensions the fallback ",[21,1766,262],{}," must carry.",[44,1769,1770,1774],{},[30,1771,1773],{"href":1772},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites\u002F","Reducing LCP From Hero Images on Static Sites"," — what to do with the hero once it is small.",[44,1776,1777,1779],{},[30,1778,1109],{"href":1108}," — immutable caching for hashed variants.",[1781,1782,1783],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}",{"title":86,"searchDepth":107,"depth":107,"links":1785},[1786,1787,1788,1789,1790,1791,1792,1793,1794,1801],{"id":38,"depth":107,"text":39},{"id":63,"depth":107,"text":64},{"id":437,"depth":107,"text":438},{"id":1113,"depth":107,"text":1114},{"id":1258,"depth":107,"text":1259},{"id":1447,"depth":107,"text":1448},{"id":1611,"depth":107,"text":1612},{"id":1670,"depth":107,"text":1671},{"id":1682,"depth":107,"text":1683,"children":1795},[1796,1797,1798,1799,1800],{"id":1687,"depth":135,"text":1688},{"id":1694,"depth":135,"text":1695},{"id":1701,"depth":135,"text":1702},{"id":1714,"depth":135,"text":1715},{"id":1733,"depth":135,"text":1734},{"id":1740,"depth":107,"text":1741},[1803,1805,1808,1809],{"name":1804,"item":731},"Home",{"name":1806,"item":1807},"Performance Optimization & Core Web Vitals for SSGs","\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002F",{"name":33,"item":32},{"name":5,"item":1810},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites\u002F","2026-08-01","Emit AVIF, WebP and a legacy fallback from one build step, order picture sources correctly, keep Vary headers honest, and measure the real byte and LCP savings.","md",[1815,1816,1817,1819,1821],{"q":1688,"a":1691},{"q":1695,"a":1698},{"q":1702,"a":1818},"The browser takes the first source whose type it supports, so listing WebP before AVIF means AVIF-capable browsers still get WebP. Order sources from most to least efficient, with the plain img element last as the universal fallback.",{"q":1715,"a":1820},"Not with the picture element, because the browser chooses and each variant has its own URL. Vary matters only when a server or CDN rewrites a single URL based on the Accept header, which is where mis-set Vary silently poisons caches.",{"q":1734,"a":1737},{},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites",{"title":5,"description":1812},"performance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites\u002Findex","article","97bQJKmkAu-nvAckQCB2Gegzzq1OkKRYbv8hvFcMdLU",[1829,1832,1835,1838,1841,1844,1847,1850,1853,1856,1859,1862,1865,1868,1871,1874,1877,1880,1883,1886,1889,1892,1895,1898,1901,1904,1907,1909,1912,1915,1918,1921,1924,1927,1930,1932,1935,1938,1941,1944,1946,1948,1949,1952,1955,1958,1961,1964,1967,1970,1973,1976,1979,1982,1985,1988,1991,1994,1997,2000,2003,2006,2009,2012,2015,2018,2021,2024,2027,2030,2033,2036,2039,2042,2045,2048,2051,2054,2057,2060,2063,2066,2069],{"path":1830,"title":1831},"\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":1833,"title":1834},"\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":1836,"title":1837},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1839,"title":1840},"\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":1842,"title":1843},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1845,"title":1846},"\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":1848,"title":1849},"\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":1851,"title":1852},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1854,"title":1855},"\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":1857,"title":1858},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1860,"title":1861},"\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":1863,"title":1864},"\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":1866,"title":1867},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1869,"title":1870},"\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":1872,"title":1873},"\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":1875,"title":1876},"\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":1878,"title":1879},"\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":1881,"title":1882},"\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":1884,"title":1885},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1887,"title":1888},"\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":1890,"title":1891},"\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":1893,"title":1894},"\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":1896,"title":1897},"\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":1899,"title":1900},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1902,"title":1903},"\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":1905,"title":1906},"\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":1908,"title":1109},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs",{"path":1910,"title":1911},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1913,"title":1914},"\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":1916,"title":1917},"\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":1919,"title":1920},"\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":1922,"title":1923},"\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":1925,"title":1926},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1928,"title":1929},"\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":1931,"title":275},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift",{"path":1933,"title":1934},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1936,"title":1937},"\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":1939,"title":1940},"\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":1942,"title":1943},"\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":1945,"title":33},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro",{"path":1947,"title":1758},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins",{"path":1823,"title":5},{"path":1950,"title":1951},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1953,"title":1954},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1956,"title":1957},"\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":1959,"title":1960},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1962,"title":1963},"\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":1965,"title":1966},"\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":1968,"title":1969},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1971,"title":1972},"\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":1974,"title":1975},"\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":1977,"title":1978},"\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":1980,"title":1981},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1983,"title":1984},"\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":1986,"title":1987},"\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":1989,"title":1990},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1992,"title":1993},"\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":1995,"title":1996},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1998,"title":1999},"\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":2001,"title":2002},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":2004,"title":2005},"\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":2007,"title":2008},"\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":2010,"title":2011},"\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":2013,"title":2014},"\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":2016,"title":2017},"\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":2019,"title":2020},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":2022,"title":2023},"\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":2025,"title":2026},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":2028,"title":2029},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":2031,"title":2032},"\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":2034,"title":2035},"\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":2037,"title":2038},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":2040,"title":2041},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":2043,"title":2044},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":2046,"title":2047},"\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":2049,"title":2050},"\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":2052,"title":2053},"\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":2055,"title":2056},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":2058,"title":2059},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":2061,"title":2062},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":2064,"title":2065},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":2067,"title":2068},"\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":2070,"title":2071},"\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",1785611671447]