[{"data":1,"prerenderedAt":7291},["ShallowReactive",2],{"posts:3":3},[4,1770,3564],{"id":5,"title":6,"body":7,"date":1760,"description":1761,"draft":1762,"extension":1763,"meta":1764,"navigation":150,"path":1765,"readingTime":1766,"seo":1767,"stem":1768,"__hash__":1769},"posts\u002Fblog\u002Fworkbox-astro-project.md","Integrate Workbox Service Worker into Astro Project",{"type":8,"value":9,"toc":1755},"minimark",[10,28,33,65,71,100,110,116,270,298,311,316,368,391,397,407,414,614,621,636,639,648,652,693,719,727,796,822,941,953,995,1004,1012,1114,1123,1138,1160,1301,1304,1348,1354,1361,1369,1700,1703,1711,1715,1733,1736,1751],[11,12,13,14,21,22,27],"p",{},"Integrating a\n",[15,16,20],"a",{"href":17,"rel":18},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FService_Worker_API",[19],"nofollow","Service worker","\nto your website is a great way to progressively enhance the sites performance as\nwell as supporting features like offline content viewing, background push etc.\nService Workers even though as simple as they are can be a bit daunting at first\nand a bit tricky to deal with especially when it comes to things like\npre-caching, pruning old caches or even handling events across the service\nworker and web app. ",[15,23,26],{"href":24,"rel":25},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002F",[19],"Workbox"," is a\nset of well tested production ready tools\u002Flibraries that helps simplify these\nthings.",[29,30,32],"h2",{"id":31},"installing-workbox","Installing Workbox",[11,34,35,36,44,45,52,53,56,57,64],{},"Workbox can be setup in various ways like using\n",[15,37,40],{"href":38,"rel":39},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fmodules\u002Fworkbox-cli\u002F",[19],[41,42,43],"code",{},"workbox-cli",",\n",[15,46,49],{"href":47,"rel":48},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fmodules\u002Fworkbox-build\u002F",[19],[41,50,51],{},"workbox-build","\nnode package or just using a bundler plugin. All of the methods more or less do\none thing that is to generate the final ",[41,54,55],{},"sw.js"," service worker file which can be\nregistered in your HTML document using\n",[15,58,61],{"href":59,"rel":60},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FServiceWorkerContainer\u002Fregister",[19],[41,62,63],{},"navigator.serviceWorker.register","\nmethod.",[11,66,67,68,70],{},"Here i'll be using the ",[41,69,43],{}," to generate my service worker file. Let's\ninstall it using package manager.",[72,73,78],"pre",{"className":74,"code":75,"language":76,"meta":77,"style":77},"language-sh shiki shiki-themes github-dark","npm install -D workbox-cli\n","sh","",[41,79,80],{"__ignoreMap":77},[81,82,85,89,93,97],"span",{"class":83,"line":84},"line",1,[81,86,88],{"class":87},"svObZ","npm",[81,90,92],{"class":91},"sU2Wk"," install",[81,94,96],{"class":95},"sDLfK"," -D",[81,98,99],{"class":91}," workbox-cli\n",[11,101,102,103,106,107,109],{},"Create a ",[41,104,105],{},"workbox.config.cjs"," file in the root directory of your project. This\nis the config file ",[41,108,43],{}," will use to generate our service worker file.",[11,111,112,113,115],{},"Let's add some config options to ",[41,114,105],{},":",[72,117,121],{"className":118,"code":119,"language":120,"meta":77,"style":77},"language-js shiki shiki-themes github-dark","const globPath = process.env.SW_DIST_PATH;\n\nif (!globPath) {\n  console.error(\"SW_DIST_PATH not specified. Check your npm build script\");\n  return;\n}\n\nmodule.exports = {\n  globDirectory: globPath,\n  globPatterns: [\n    \"**\u002F*.{css,png,webp,avif,mp4,html,ico,woff2,json,js,svg,xml,txt}\",\n  ],\n  swDest: `${globPath}\u002Fsw.js`,\n};\n","js",[41,122,123,145,152,167,185,193,199,204,221,227,233,241,247,264],{"__ignoreMap":77},[81,124,125,129,132,135,139,142],{"class":83,"line":84},[81,126,128],{"class":127},"snl16","const",[81,130,131],{"class":95}," globPath",[81,133,134],{"class":127}," =",[81,136,138],{"class":137},"s95oV"," process.env.",[81,140,141],{"class":95},"SW_DIST_PATH",[81,143,144],{"class":137},";\n",[81,146,148],{"class":83,"line":147},2,[81,149,151],{"emptyLinePlaceholder":150},true,"\n",[81,153,155,158,161,164],{"class":83,"line":154},3,[81,156,157],{"class":127},"if",[81,159,160],{"class":137}," (",[81,162,163],{"class":127},"!",[81,165,166],{"class":137},"globPath) {\n",[81,168,170,173,176,179,182],{"class":83,"line":169},4,[81,171,172],{"class":137},"  console.",[81,174,175],{"class":87},"error",[81,177,178],{"class":137},"(",[81,180,181],{"class":91},"\"SW_DIST_PATH not specified. Check your npm build script\"",[81,183,184],{"class":137},");\n",[81,186,188,191],{"class":83,"line":187},5,[81,189,190],{"class":127},"  return",[81,192,144],{"class":137},[81,194,196],{"class":83,"line":195},6,[81,197,198],{"class":137},"}\n",[81,200,202],{"class":83,"line":201},7,[81,203,151],{"emptyLinePlaceholder":150},[81,205,207,210,213,216,218],{"class":83,"line":206},8,[81,208,209],{"class":95},"module",[81,211,212],{"class":137},".",[81,214,215],{"class":95},"exports",[81,217,134],{"class":127},[81,219,220],{"class":137}," {\n",[81,222,224],{"class":83,"line":223},9,[81,225,226],{"class":137},"  globDirectory: globPath,\n",[81,228,230],{"class":83,"line":229},10,[81,231,232],{"class":137},"  globPatterns: [\n",[81,234,236,239],{"class":83,"line":235},11,[81,237,238],{"class":91},"    \"**\u002F*.{css,png,webp,avif,mp4,html,ico,woff2,json,js,svg,xml,txt}\"",[81,240,44],{"class":137},[81,242,244],{"class":83,"line":243},12,[81,245,246],{"class":137},"  ],\n",[81,248,250,253,256,259,262],{"class":83,"line":249},13,[81,251,252],{"class":137},"  swDest: ",[81,254,255],{"class":91},"`${",[81,257,258],{"class":137},"globPath",[81,260,261],{"class":91},"}\u002Fsw.js`",[81,263,44],{"class":137},[81,265,267],{"class":83,"line":266},14,[81,268,269],{"class":137},"};\n",[11,271,272,273,276,277,280,281,283,284,286,287,290,291,293,294,297],{},"The ",[41,274,275],{},"globPatterns"," option specifies all the file types we want our service\nworker to pre-cache when it installs and ",[41,278,279],{},"globDirectory"," is the path where the\n",[41,282,43],{}," should look for to match the file type patterns, So the\n",[41,285,279],{}," is basically the build output path of our ",[41,288,289],{},"astro build"," command.\nSince we can target different adapters to build for in our Astro project I have\nmoved out the ",[41,292,279],{}," value into the environment variable for some extra\nflexibility. The ",[41,295,296],{},"swDest"," directory is where the service worker file will be\nwritten to.",[11,299,300,301,303,304,307,308,310],{},"Let's use the above config file in ",[41,302,43],{}," to generate out service worker\nfile. First we'll need to update our build scripts in ",[41,305,306],{},"package.json"," file so we\ncan specify our ",[41,309,141],{}," environment variable value.",[11,312,313,314,115],{},"In our ",[41,315,306],{},[72,317,321],{"className":318,"code":319,"language":320,"meta":77,"style":77},"language-json shiki shiki-themes github-dark","{\n  \"scripts\": {\n    \"build\": \"astro build && npm run generateSW\",\n    \"generateSW\": \"SW_DIST_PATH=dist\u002Fclient workbox generateSW workbox.config.cjs\"\n  }\n}\n","json",[41,322,323,328,336,349,359,364],{"__ignoreMap":77},[81,324,325],{"class":83,"line":84},[81,326,327],{"class":137},"{\n",[81,329,330,333],{"class":83,"line":147},[81,331,332],{"class":95},"  \"scripts\"",[81,334,335],{"class":137},": {\n",[81,337,338,341,344,347],{"class":83,"line":154},[81,339,340],{"class":95},"    \"build\"",[81,342,343],{"class":137},": ",[81,345,346],{"class":91},"\"astro build && npm run generateSW\"",[81,348,44],{"class":137},[81,350,351,354,356],{"class":83,"line":169},[81,352,353],{"class":95},"    \"generateSW\"",[81,355,343],{"class":137},[81,357,358],{"class":91},"\"SW_DIST_PATH=dist\u002Fclient workbox generateSW workbox.config.cjs\"\n",[81,360,361],{"class":83,"line":187},[81,362,363],{"class":137},"  }\n",[81,365,366],{"class":83,"line":195},[81,367,198],{"class":137},[11,369,370,371,376,377,380,381,384,385,387,388,390],{},"I'm using\n",[15,372,375],{"href":373,"rel":374},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fguides\u002Fintegrations-guide\u002Fnode\u002F",[19],"Astro Node Adapter","\nin this example that outputs build files into ",[41,378,379],{},"dist\u002Fclient"," directory. So when\nwe run ",[41,382,383],{},"generateSW"," command the workbox cli will look for static assets in the\ncorrect directory and include the file paths in the ",[41,386,55],{}," file. Now we can\ninclude this ",[41,389,55],{}," file in our Astro project and register it.",[11,392,393,394,396],{},"Generated ",[41,395,55],{}," file in the build directory:",[11,398,399],{},[400,401],"img",{"alt":402,"dataZoomable":77,"height":403,"loading":404,"src":405,"width":406},"Pre-cache urls in the generated sw.js file",1224,"lazy","\u002Fcontent\u002Fprecache-urls.png",2034,[11,408,409,410,413],{},"In your ",[41,411,412],{},"Layout.astro"," or common layout file we can register this service worker\nlike this:",[72,415,419],{"className":416,"code":417,"language":418,"meta":77,"style":77},"language-html shiki shiki-themes github-dark","\u003Chead>\n  \u003Cscript is:inline>\n    addEventListener(\"load\", () => {\n      if (\"serviceWorker\" in navigator) {\n        navigator.serviceWorker\n          .register(\"\u002Fsw.js\")\n          .then(() => {\n            console.log(\"Service Worker Registered\");\n          })\n          .catch((err) => {\n            console.log(\"Some error occurred while registering service worker\");\n            console.log(err);\n          });\n      }\n    });\n  \u003C\u002Fscript>\n\u003C\u002Fhead>\n","html",[41,420,421,433,446,464,480,485,501,515,530,535,556,569,578,583,588,594,604],{"__ignoreMap":77},[81,422,423,426,430],{"class":83,"line":84},[81,424,425],{"class":137},"\u003C",[81,427,429],{"class":428},"s4JwU","head",[81,431,432],{"class":137},">\n",[81,434,435,438,441,444],{"class":83,"line":147},[81,436,437],{"class":137},"  \u003C",[81,439,440],{"class":428},"script",[81,442,443],{"class":87}," is:inline",[81,445,432],{"class":137},[81,447,448,451,453,456,459,462],{"class":83,"line":154},[81,449,450],{"class":87},"    addEventListener",[81,452,178],{"class":137},[81,454,455],{"class":91},"\"load\"",[81,457,458],{"class":137},", () ",[81,460,461],{"class":127},"=>",[81,463,220],{"class":137},[81,465,466,469,471,474,477],{"class":83,"line":169},[81,467,468],{"class":127},"      if",[81,470,160],{"class":137},[81,472,473],{"class":91},"\"serviceWorker\"",[81,475,476],{"class":127}," in",[81,478,479],{"class":137}," navigator) {\n",[81,481,482],{"class":83,"line":187},[81,483,484],{"class":137},"        navigator.serviceWorker\n",[81,486,487,490,493,495,498],{"class":83,"line":195},[81,488,489],{"class":137},"          .",[81,491,492],{"class":87},"register",[81,494,178],{"class":137},[81,496,497],{"class":91},"\"\u002Fsw.js\"",[81,499,500],{"class":137},")\n",[81,502,503,505,508,511,513],{"class":83,"line":201},[81,504,489],{"class":137},[81,506,507],{"class":87},"then",[81,509,510],{"class":137},"(() ",[81,512,461],{"class":127},[81,514,220],{"class":137},[81,516,517,520,523,525,528],{"class":83,"line":206},[81,518,519],{"class":137},"            console.",[81,521,522],{"class":87},"log",[81,524,178],{"class":137},[81,526,527],{"class":91},"\"Service Worker Registered\"",[81,529,184],{"class":137},[81,531,532],{"class":83,"line":223},[81,533,534],{"class":137},"          })\n",[81,536,537,539,542,545,549,552,554],{"class":83,"line":229},[81,538,489],{"class":137},[81,540,541],{"class":87},"catch",[81,543,544],{"class":137},"((",[81,546,548],{"class":547},"s9osk","err",[81,550,551],{"class":137},") ",[81,553,461],{"class":127},[81,555,220],{"class":137},[81,557,558,560,562,564,567],{"class":83,"line":235},[81,559,519],{"class":137},[81,561,522],{"class":87},[81,563,178],{"class":137},[81,565,566],{"class":91},"\"Some error occurred while registering service worker\"",[81,568,184],{"class":137},[81,570,571,573,575],{"class":83,"line":243},[81,572,519],{"class":137},[81,574,522],{"class":87},[81,576,577],{"class":137},"(err);\n",[81,579,580],{"class":83,"line":249},[81,581,582],{"class":137},"          });\n",[81,584,585],{"class":83,"line":266},[81,586,587],{"class":137},"      }\n",[81,589,591],{"class":83,"line":590},15,[81,592,593],{"class":137},"    });\n",[81,595,597,600,602],{"class":83,"line":596},16,[81,598,599],{"class":137},"  \u003C\u002F",[81,601,440],{"class":428},[81,603,432],{"class":137},[81,605,607,610,612],{"class":83,"line":606},17,[81,608,609],{"class":137},"\u003C\u002F",[81,611,429],{"class":428},[81,613,432],{"class":137},[11,615,616,617,620],{},"If we run the preview server (",[41,618,619],{},"npm run preview",") and check our Dev Tools we can\nsee that our service worker is registered and installing.",[11,622,623,629,630],{},[400,624],{"alt":625,"dataZoomable":77,"height":626,"loading":404,"src":627,"width":628},"Service Worker installing",686,"\u002Fcontent\u002Fsw-installing.png",1634,"\nService Worker Pre-caching:\n",[400,631],{"alt":632,"dataZoomable":77,"height":633,"loading":404,"src":634,"width":635},"Service Worker Precaching",702,"\u002Fcontent\u002Fsw-precaching.png",3076,[11,637,638],{},"Once the service worker is done pre-caching all the assets it will be activated\nand running.",[11,640,641,647],{},[400,642],{"alt":643,"dataZoomable":77,"height":644,"loading":404,"src":645,"width":646},"Service Worker Activated",670,"\u002Fcontent\u002Fsw-activated.png",1324,"\nNow any requests that are pre-cached will be returned by our service worker. Workbox\nhandles all the pruning of old caches, revisions, updating sw.js etc for us.",[29,649,651],{"id":650},"customising-our-service-worker","Customising our service worker",[11,653,654,655,660,661,44,663,669,670,677,678,680,681,683,684,686,687,689,690,692],{},"There are mainly\n",[15,656,659],{"href":657,"rel":658},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fthe-ways-of-workbox\u002F",[19],"two ways of generating","\nour service worker using ",[41,662,43],{},[15,664,667],{"href":665,"rel":666},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fmodules\u002Fworkbox-cli\u002F#generatesw",[19],[41,668,383],{},"\nand\n",[15,671,674],{"href":672,"rel":673},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fmodules\u002Fworkbox-cli\u002F#injectmanifest",[19],[41,675,676],{},"injectManifest",".\nSince we use ",[41,679,383],{}," command from the ",[41,682,43],{}," we don't have much\ncontrol over the final ",[41,685,55],{}," file. If we want to add some custom logic to our\nservice worker along with workbox tools we can use ",[41,688,676],{}," command.\nThis let's us pass in our custom logic into the final ",[41,691,55],{}," file",[11,694,695,696,698,699,702,703,708,709,711,712,714,715,718],{},"Let's create a custom service worker using ",[41,697,43],{}," but still keep the\npre-caching capabilities of workbox. That's where ",[41,700,701],{},"workbox-modules"," come into\nplay. Pre-caching routes is actually a\n",[15,704,707],{"href":705,"rel":706},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fmodules\u002F",[19],"workbox module"," that we get\nfor free while using ",[41,710,383],{}," command from workbox. First let's create a\n",[41,713,55],{}," file in ",[41,716,717],{},"public"," directory of our astro project and add pre-caching\nmodule from workbox to it.",[11,720,721,723,724,726],{},[41,722,55],{}," in ",[41,725,717],{}," directory:",[72,728,730],{"className":118,"code":729,"language":120,"meta":77,"style":77},"import { precacheAndRoute, cleanupOutdatedCaches } from \"workbox-precaching\";\n\ncleanupOutdatedCaches();\n\n\u002F\u002F Precache the manifest\nprecacheAndRoute(self.__WB_MANIFEST);\n\nconsole.log(\"Hello. Some custom logic here...\");\n",[41,731,732,748,752,760,764,770,778,782],{"__ignoreMap":77},[81,733,734,737,740,743,746],{"class":83,"line":84},[81,735,736],{"class":127},"import",[81,738,739],{"class":137}," { precacheAndRoute, cleanupOutdatedCaches } ",[81,741,742],{"class":127},"from",[81,744,745],{"class":91}," \"workbox-precaching\"",[81,747,144],{"class":137},[81,749,750],{"class":83,"line":147},[81,751,151],{"emptyLinePlaceholder":150},[81,753,754,757],{"class":83,"line":154},[81,755,756],{"class":87},"cleanupOutdatedCaches",[81,758,759],{"class":137},"();\n",[81,761,762],{"class":83,"line":169},[81,763,151],{"emptyLinePlaceholder":150},[81,765,766],{"class":83,"line":187},[81,767,769],{"class":768},"sAwPA","\u002F\u002F Precache the manifest\n",[81,771,772,775],{"class":83,"line":195},[81,773,774],{"class":87},"precacheAndRoute",[81,776,777],{"class":137},"(self.__WB_MANIFEST);\n",[81,779,780],{"class":83,"line":201},[81,781,151],{"emptyLinePlaceholder":150},[81,783,784,787,789,791,794],{"class":83,"line":206},[81,785,786],{"class":137},"console.",[81,788,522],{"class":87},[81,790,178],{"class":137},[81,792,793],{"class":91},"\"Hello. Some custom logic here...\"",[81,795,184],{"class":137},[11,797,798,799,802,803,805,806,808,809,815,816,818,819,821],{},"Here ",[41,800,801],{},"self.__WB_MANIFEST"," is a special value where workbox will inject the list\nof URLs that are needed to be pre-cached using the ",[41,804,279],{}," and\n",[41,807,275],{}," config.\n",[15,810,813],{"href":811,"rel":812},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Freference\u002Fworkbox-precaching\u002F#method-cleanupOutdatedCaches",[19],[41,814,756],{},"\nmethod will clean-up old and incompatible caches created previously by older\nversions of workbox. Let's add this ",[41,817,55],{}," file as input in our\n",[41,820,105],{}," file:",[72,823,825],{"className":118,"code":824,"language":120,"meta":77,"style":77},"const globPath = process.env.SW_DIST_PATH;\n\nif (!globPath) {\n  console.error(\"SW_DIST_PATH not specified. Check your npm build script\");\n  return;\n}\n\nmodule.exports = {\n  globDirectory: globPath,\n  globPatterns: [\n    \"**\u002F*.{css,png,webp,avif,mp4,html,ico,woff2,json,js,svg,xml,txt}\",\n  ],\n  swDest: `${globPath}\u002Fsw.js`,\n  swSrc: \".\u002Fpublic\u002Fsw.js\", \u002F\u002F Our custom sw.js file\n};\n",[41,826,827,841,845,855,867,873,877,881,893,897,901,907,911,923,937],{"__ignoreMap":77},[81,828,829,831,833,835,837,839],{"class":83,"line":84},[81,830,128],{"class":127},[81,832,131],{"class":95},[81,834,134],{"class":127},[81,836,138],{"class":137},[81,838,141],{"class":95},[81,840,144],{"class":137},[81,842,843],{"class":83,"line":147},[81,844,151],{"emptyLinePlaceholder":150},[81,846,847,849,851,853],{"class":83,"line":154},[81,848,157],{"class":127},[81,850,160],{"class":137},[81,852,163],{"class":127},[81,854,166],{"class":137},[81,856,857,859,861,863,865],{"class":83,"line":169},[81,858,172],{"class":137},[81,860,175],{"class":87},[81,862,178],{"class":137},[81,864,181],{"class":91},[81,866,184],{"class":137},[81,868,869,871],{"class":83,"line":187},[81,870,190],{"class":127},[81,872,144],{"class":137},[81,874,875],{"class":83,"line":195},[81,876,198],{"class":137},[81,878,879],{"class":83,"line":201},[81,880,151],{"emptyLinePlaceholder":150},[81,882,883,885,887,889,891],{"class":83,"line":206},[81,884,209],{"class":95},[81,886,212],{"class":137},[81,888,215],{"class":95},[81,890,134],{"class":127},[81,892,220],{"class":137},[81,894,895],{"class":83,"line":223},[81,896,226],{"class":137},[81,898,899],{"class":83,"line":229},[81,900,232],{"class":137},[81,902,903,905],{"class":83,"line":235},[81,904,238],{"class":91},[81,906,44],{"class":137},[81,908,909],{"class":83,"line":243},[81,910,246],{"class":137},[81,912,913,915,917,919,921],{"class":83,"line":249},[81,914,252],{"class":137},[81,916,255],{"class":91},[81,918,258],{"class":137},[81,920,261],{"class":91},[81,922,44],{"class":137},[81,924,925,928,931,934],{"class":83,"line":266},[81,926,927],{"class":137},"  swSrc: ",[81,929,930],{"class":91},"\".\u002Fpublic\u002Fsw.js\"",[81,932,933],{"class":137},", ",[81,935,936],{"class":768},"\u002F\u002F Our custom sw.js file\n",[81,938,939],{"class":83,"line":590},[81,940,269],{"class":137},[11,942,943,944,946,947,949,950,952],{},"Also let's change the npm package scripts from ",[41,945,383],{}," to ",[41,948,676],{},"\nin our ",[41,951,43],{}," command:",[72,954,956],{"className":318,"code":955,"language":320,"meta":77,"style":77},"{\n  \"scripts\": {\n    \"build\": \"astro build && npm run generateSW\",\n    \"generateSW\": \"SW_DIST_PATH=dist\u002Fclient workbox injectManifest workbox.config.cjs\"\n  }\n}\n",[41,957,958,962,968,978,987,991],{"__ignoreMap":77},[81,959,960],{"class":83,"line":84},[81,961,327],{"class":137},[81,963,964,966],{"class":83,"line":147},[81,965,332],{"class":95},[81,967,335],{"class":137},[81,969,970,972,974,976],{"class":83,"line":154},[81,971,340],{"class":95},[81,973,343],{"class":137},[81,975,346],{"class":91},[81,977,44],{"class":137},[81,979,980,982,984],{"class":83,"line":169},[81,981,353],{"class":95},[81,983,343],{"class":137},[81,985,986],{"class":91},"\"SW_DIST_PATH=dist\u002Fclient workbox injectManifest workbox.config.cjs\"\n",[81,988,989],{"class":83,"line":187},[81,990,363],{"class":137},[81,992,993],{"class":83,"line":195},[81,994,198],{"class":137},[11,996,997,998,1000,1001,1003],{},"Now if we build the astro project using ",[41,999,289],{}," we should get an ",[41,1002,55],{},"\nfile in the build output directory with our custom service worker logic and\nworkbox pre-caching capabilities.",[11,1005,1006,1007,1009,1010,115],{},"Built ",[41,1008,55],{}," file from ",[41,1011,379],{},[72,1013,1015],{"className":118,"code":1014,"language":120,"meta":77,"style":77},"import { precacheAndRoute } from \"workbox-precaching\";\n\u002F\u002F Precache the manifest\nprecacheAndRoute([\n  {\n    revision: \"6f5efcdd674ddc792168251976c87ca1\",\n    url: \"_astro\u002F_slug_.320abdb5.css\",\n  },\n  { revision: \"307aca520857f32f274358d4e881e381\", url: \"uses\u002Findex.html\" },\n  \u002F\u002F Some more Pre-cache urls...\n]);\n\nconsole.log(\"Hello. Some custom logic here...\");\n",[41,1016,1017,1030,1034,1041,1046,1056,1066,1071,1088,1093,1098,1102],{"__ignoreMap":77},[81,1018,1019,1021,1024,1026,1028],{"class":83,"line":84},[81,1020,736],{"class":127},[81,1022,1023],{"class":137}," { precacheAndRoute } ",[81,1025,742],{"class":127},[81,1027,745],{"class":91},[81,1029,144],{"class":137},[81,1031,1032],{"class":83,"line":147},[81,1033,769],{"class":768},[81,1035,1036,1038],{"class":83,"line":154},[81,1037,774],{"class":87},[81,1039,1040],{"class":137},"([\n",[81,1042,1043],{"class":83,"line":169},[81,1044,1045],{"class":137},"  {\n",[81,1047,1048,1051,1054],{"class":83,"line":187},[81,1049,1050],{"class":137},"    revision: ",[81,1052,1053],{"class":91},"\"6f5efcdd674ddc792168251976c87ca1\"",[81,1055,44],{"class":137},[81,1057,1058,1061,1064],{"class":83,"line":195},[81,1059,1060],{"class":137},"    url: ",[81,1062,1063],{"class":91},"\"_astro\u002F_slug_.320abdb5.css\"",[81,1065,44],{"class":137},[81,1067,1068],{"class":83,"line":201},[81,1069,1070],{"class":137},"  },\n",[81,1072,1073,1076,1079,1082,1085],{"class":83,"line":206},[81,1074,1075],{"class":137},"  { revision: ",[81,1077,1078],{"class":91},"\"307aca520857f32f274358d4e881e381\"",[81,1080,1081],{"class":137},", url: ",[81,1083,1084],{"class":91},"\"uses\u002Findex.html\"",[81,1086,1087],{"class":137}," },\n",[81,1089,1090],{"class":83,"line":223},[81,1091,1092],{"class":768},"  \u002F\u002F Some more Pre-cache urls...\n",[81,1094,1095],{"class":83,"line":229},[81,1096,1097],{"class":137},"]);\n",[81,1099,1100],{"class":83,"line":235},[81,1101,151],{"emptyLinePlaceholder":150},[81,1103,1104,1106,1108,1110,1112],{"class":83,"line":243},[81,1105,786],{"class":137},[81,1107,522],{"class":87},[81,1109,178],{"class":137},[81,1111,793],{"class":91},[81,1113,184],{"class":137},[11,1115,1116,1117,1119,1120,212],{},"If we start the preview server (",[41,1118,619],{},") we can see that our service\nworker doesn't register and throws out an error:\n",[41,1121,1122],{},"Uncaught SyntaxError: Cannot use import statement outside a module (at sw.js:1:1)",[11,1124,1125,1131,1132,1134,1135,1137],{},[400,1126],{"alt":1127,"dataZoomable":77,"height":1128,"loading":404,"src":1129,"width":1130},"Error registering the service worker",90,"\u002Fcontent\u002FinjectManifest-error.png",2668,"\nThat is because when we use ",[41,1133,676],{}," workbox doesn't bundle the dependencies\nso we need to bundle the dependencies and compile the final ",[41,1136,55],{}," ourselves.",[11,1139,1140,1141,1148,1149,1152,1153,1156,1157,1159],{},"We can use any bundler that we like, I'll be using\n",[15,1142,1145],{"href":1143,"rel":1144},"https:\u002F\u002Fesbuild.github.io",[19],[41,1146,1147],{},"esbuild"," for this. Install esbuild using\n",[41,1150,1151],{},"npm install --save-exact esbuild"," and create a ",[41,1154,1155],{},"bundle-sw.mjs"," file and\nconfigure it to bundle our final ",[41,1158,55],{}," file like so:",[72,1161,1163],{"className":118,"code":1162,"language":120,"meta":77,"style":77},"import * as esbuild from \"esbuild\";\nconst globPath = process.env.SW_DIST_PATH;\n\n\u002F\u002F bundle the sw file for browser use\nawait esbuild.build({\n  entryPoints: [`${globPath}\u002Fsw.js`],\n  outfile: `${globPath}\u002Fsw.js`,\n  target: [\"es2020\"],\n  bundle: true,\n  minify: true,\n  allowOverwrite: true,\n  sourcemap: true,\n});\n",[41,1164,1165,1185,1199,1203,1208,1222,1236,1249,1259,1269,1278,1287,1296],{"__ignoreMap":77},[81,1166,1167,1169,1172,1175,1178,1180,1183],{"class":83,"line":84},[81,1168,736],{"class":127},[81,1170,1171],{"class":95}," *",[81,1173,1174],{"class":127}," as",[81,1176,1177],{"class":137}," esbuild ",[81,1179,742],{"class":127},[81,1181,1182],{"class":91}," \"esbuild\"",[81,1184,144],{"class":137},[81,1186,1187,1189,1191,1193,1195,1197],{"class":83,"line":147},[81,1188,128],{"class":127},[81,1190,131],{"class":95},[81,1192,134],{"class":127},[81,1194,138],{"class":137},[81,1196,141],{"class":95},[81,1198,144],{"class":137},[81,1200,1201],{"class":83,"line":154},[81,1202,151],{"emptyLinePlaceholder":150},[81,1204,1205],{"class":83,"line":169},[81,1206,1207],{"class":768},"\u002F\u002F bundle the sw file for browser use\n",[81,1209,1210,1213,1216,1219],{"class":83,"line":187},[81,1211,1212],{"class":127},"await",[81,1214,1215],{"class":137}," esbuild.",[81,1217,1218],{"class":87},"build",[81,1220,1221],{"class":137},"({\n",[81,1223,1224,1227,1229,1231,1233],{"class":83,"line":195},[81,1225,1226],{"class":137},"  entryPoints: [",[81,1228,255],{"class":91},[81,1230,258],{"class":137},[81,1232,261],{"class":91},[81,1234,1235],{"class":137},"],\n",[81,1237,1238,1241,1243,1245,1247],{"class":83,"line":201},[81,1239,1240],{"class":137},"  outfile: ",[81,1242,255],{"class":91},[81,1244,258],{"class":137},[81,1246,261],{"class":91},[81,1248,44],{"class":137},[81,1250,1251,1254,1257],{"class":83,"line":206},[81,1252,1253],{"class":137},"  target: [",[81,1255,1256],{"class":91},"\"es2020\"",[81,1258,1235],{"class":137},[81,1260,1261,1264,1267],{"class":83,"line":223},[81,1262,1263],{"class":137},"  bundle: ",[81,1265,1266],{"class":95},"true",[81,1268,44],{"class":137},[81,1270,1271,1274,1276],{"class":83,"line":229},[81,1272,1273],{"class":137},"  minify: ",[81,1275,1266],{"class":95},[81,1277,44],{"class":137},[81,1279,1280,1283,1285],{"class":83,"line":235},[81,1281,1282],{"class":137},"  allowOverwrite: ",[81,1284,1266],{"class":95},[81,1286,44],{"class":137},[81,1288,1289,1292,1294],{"class":83,"line":243},[81,1290,1291],{"class":137},"  sourcemap: ",[81,1293,1266],{"class":95},[81,1295,44],{"class":137},[81,1297,1298],{"class":83,"line":249},[81,1299,1300],{"class":137},"});\n",[11,1302,1303],{},"Let's now modify our npm package script to add esbuild bundle stage.",[72,1305,1307],{"className":318,"code":1306,"language":320,"meta":77,"style":77},"{\n  \"scripts\": {\n    \"build\": \"astro build && npm run generateAndBundleSW\",\n    \"generateAndBundleSW\": \"SW_DIST_PATH=dist\u002Fclient workbox injectManifest workbox.config.cjs && node bundle-sw.mjs\"\n  }\n}\n",[41,1308,1309,1313,1319,1330,1340,1344],{"__ignoreMap":77},[81,1310,1311],{"class":83,"line":84},[81,1312,327],{"class":137},[81,1314,1315,1317],{"class":83,"line":147},[81,1316,332],{"class":95},[81,1318,335],{"class":137},[81,1320,1321,1323,1325,1328],{"class":83,"line":154},[81,1322,340],{"class":95},[81,1324,343],{"class":137},[81,1326,1327],{"class":91},"\"astro build && npm run generateAndBundleSW\"",[81,1329,44],{"class":137},[81,1331,1332,1335,1337],{"class":83,"line":169},[81,1333,1334],{"class":95},"    \"generateAndBundleSW\"",[81,1336,343],{"class":137},[81,1338,1339],{"class":91},"\"SW_DIST_PATH=dist\u002Fclient workbox injectManifest workbox.config.cjs && node bundle-sw.mjs\"\n",[81,1341,1342],{"class":83,"line":187},[81,1343,363],{"class":137},[81,1345,1346],{"class":83,"line":195},[81,1347,198],{"class":137},[11,1349,1350,1351,1353],{},"Now if we ",[41,1352,289],{}," and preview the build we should have our custom service\nworker working.",[11,1355,1356,1357,1360],{},"Since we now can add custom logic to our service worker let's add another\nworkbox module that adds Navigation Preload capabilities to our site with the\nhelp of ",[41,1358,1359],{},"workbox-navigation-preload"," module. Navigation Preload fixes the delay\nin navigation requests caused by service worker boot time.",[11,1362,1363,1364,115],{},"The following example is from\n",[15,1365,1368],{"href":1366,"rel":1367},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002Fnavigation-preload\u002F",[19],"Navigation Preload for Network-first HTML",[72,1370,1372],{"className":118,"code":1371,"language":120,"meta":77,"style":77},"\u002F\u002F public\u002Fsw.js\nimport * as navigationPreload from \"workbox-navigation-preload\";\nimport { NetworkFirst, StaleWhileRevalidate } from \"workbox-strategies\";\nimport { registerRoute, NavigationRoute, Route } from \"workbox-routing\";\nimport { precacheAndRoute } from \"workbox-precaching\";\n\n\u002F\u002F Precache the manifest\nprecacheAndRoute(self.__WB_MANIFEST);\n\n\u002F\u002F Enable navigation preload\nnavigationPreload.enable();\n\n\u002F\u002F Create a new navigation route that uses the Network-first, falling back to\n\u002F\u002F cache strategy for navigation requests with its own cache. This route will be\n\u002F\u002F handled by navigation preload. The NetworkOnly strategy will work as well.\nconst navigationRoute = new NavigationRoute(\n  new NetworkFirst({\n    cacheName: \"navigations\",\n  })\n);\n\n\u002F\u002F Register the navigation route\nregisterRoute(navigationRoute);\n\n\u002F\u002F Create a route for image, script, or style requests that use a\n\u002F\u002F stale-while-revalidate strategy. This route will be unaffected\n\u002F\u002F by navigation preload.\nconst staticAssetsRoute = new Route(\n  ({ request }) => {\n    return [\"image\", \"script\", \"style\"].includes(request.destination);\n  },\n  new StaleWhileRevalidate({\n    cacheName: \"static-assets\",\n  })\n);\n\n\u002F\u002F Register the route handling static assets\nregisterRoute(staticAssetsRoute);\n",[41,1373,1374,1379,1397,1411,1425,1437,1441,1445,1451,1455,1460,1470,1474,1479,1484,1489,1507,1517,1528,1534,1539,1544,1550,1559,1564,1570,1576,1582,1599,1615,1646,1651,1661,1671,1676,1681,1686,1692],{"__ignoreMap":77},[81,1375,1376],{"class":83,"line":84},[81,1377,1378],{"class":768},"\u002F\u002F public\u002Fsw.js\n",[81,1380,1381,1383,1385,1387,1390,1392,1395],{"class":83,"line":147},[81,1382,736],{"class":127},[81,1384,1171],{"class":95},[81,1386,1174],{"class":127},[81,1388,1389],{"class":137}," navigationPreload ",[81,1391,742],{"class":127},[81,1393,1394],{"class":91}," \"workbox-navigation-preload\"",[81,1396,144],{"class":137},[81,1398,1399,1401,1404,1406,1409],{"class":83,"line":154},[81,1400,736],{"class":127},[81,1402,1403],{"class":137}," { NetworkFirst, StaleWhileRevalidate } ",[81,1405,742],{"class":127},[81,1407,1408],{"class":91}," \"workbox-strategies\"",[81,1410,144],{"class":137},[81,1412,1413,1415,1418,1420,1423],{"class":83,"line":169},[81,1414,736],{"class":127},[81,1416,1417],{"class":137}," { registerRoute, NavigationRoute, Route } ",[81,1419,742],{"class":127},[81,1421,1422],{"class":91}," \"workbox-routing\"",[81,1424,144],{"class":137},[81,1426,1427,1429,1431,1433,1435],{"class":83,"line":187},[81,1428,736],{"class":127},[81,1430,1023],{"class":137},[81,1432,742],{"class":127},[81,1434,745],{"class":91},[81,1436,144],{"class":137},[81,1438,1439],{"class":83,"line":195},[81,1440,151],{"emptyLinePlaceholder":150},[81,1442,1443],{"class":83,"line":201},[81,1444,769],{"class":768},[81,1446,1447,1449],{"class":83,"line":206},[81,1448,774],{"class":87},[81,1450,777],{"class":137},[81,1452,1453],{"class":83,"line":223},[81,1454,151],{"emptyLinePlaceholder":150},[81,1456,1457],{"class":83,"line":229},[81,1458,1459],{"class":768},"\u002F\u002F Enable navigation preload\n",[81,1461,1462,1465,1468],{"class":83,"line":235},[81,1463,1464],{"class":137},"navigationPreload.",[81,1466,1467],{"class":87},"enable",[81,1469,759],{"class":137},[81,1471,1472],{"class":83,"line":243},[81,1473,151],{"emptyLinePlaceholder":150},[81,1475,1476],{"class":83,"line":249},[81,1477,1478],{"class":768},"\u002F\u002F Create a new navigation route that uses the Network-first, falling back to\n",[81,1480,1481],{"class":83,"line":266},[81,1482,1483],{"class":768},"\u002F\u002F cache strategy for navigation requests with its own cache. This route will be\n",[81,1485,1486],{"class":83,"line":590},[81,1487,1488],{"class":768},"\u002F\u002F handled by navigation preload. The NetworkOnly strategy will work as well.\n",[81,1490,1491,1493,1496,1498,1501,1504],{"class":83,"line":596},[81,1492,128],{"class":127},[81,1494,1495],{"class":95}," navigationRoute",[81,1497,134],{"class":127},[81,1499,1500],{"class":127}," new",[81,1502,1503],{"class":87}," NavigationRoute",[81,1505,1506],{"class":137},"(\n",[81,1508,1509,1512,1515],{"class":83,"line":606},[81,1510,1511],{"class":127},"  new",[81,1513,1514],{"class":87}," NetworkFirst",[81,1516,1221],{"class":137},[81,1518,1520,1523,1526],{"class":83,"line":1519},18,[81,1521,1522],{"class":137},"    cacheName: ",[81,1524,1525],{"class":91},"\"navigations\"",[81,1527,44],{"class":137},[81,1529,1531],{"class":83,"line":1530},19,[81,1532,1533],{"class":137},"  })\n",[81,1535,1537],{"class":83,"line":1536},20,[81,1538,184],{"class":137},[81,1540,1542],{"class":83,"line":1541},21,[81,1543,151],{"emptyLinePlaceholder":150},[81,1545,1547],{"class":83,"line":1546},22,[81,1548,1549],{"class":768},"\u002F\u002F Register the navigation route\n",[81,1551,1553,1556],{"class":83,"line":1552},23,[81,1554,1555],{"class":87},"registerRoute",[81,1557,1558],{"class":137},"(navigationRoute);\n",[81,1560,1562],{"class":83,"line":1561},24,[81,1563,151],{"emptyLinePlaceholder":150},[81,1565,1567],{"class":83,"line":1566},25,[81,1568,1569],{"class":768},"\u002F\u002F Create a route for image, script, or style requests that use a\n",[81,1571,1573],{"class":83,"line":1572},26,[81,1574,1575],{"class":768},"\u002F\u002F stale-while-revalidate strategy. This route will be unaffected\n",[81,1577,1579],{"class":83,"line":1578},27,[81,1580,1581],{"class":768},"\u002F\u002F by navigation preload.\n",[81,1583,1585,1587,1590,1592,1594,1597],{"class":83,"line":1584},28,[81,1586,128],{"class":127},[81,1588,1589],{"class":95}," staticAssetsRoute",[81,1591,134],{"class":127},[81,1593,1500],{"class":127},[81,1595,1596],{"class":87}," Route",[81,1598,1506],{"class":137},[81,1600,1602,1605,1608,1611,1613],{"class":83,"line":1601},29,[81,1603,1604],{"class":137},"  ({ ",[81,1606,1607],{"class":547},"request",[81,1609,1610],{"class":137}," }) ",[81,1612,461],{"class":127},[81,1614,220],{"class":137},[81,1616,1618,1621,1624,1627,1629,1632,1634,1637,1640,1643],{"class":83,"line":1617},30,[81,1619,1620],{"class":127},"    return",[81,1622,1623],{"class":137}," [",[81,1625,1626],{"class":91},"\"image\"",[81,1628,933],{"class":137},[81,1630,1631],{"class":91},"\"script\"",[81,1633,933],{"class":137},[81,1635,1636],{"class":91},"\"style\"",[81,1638,1639],{"class":137},"].",[81,1641,1642],{"class":87},"includes",[81,1644,1645],{"class":137},"(request.destination);\n",[81,1647,1649],{"class":83,"line":1648},31,[81,1650,1070],{"class":137},[81,1652,1654,1656,1659],{"class":83,"line":1653},32,[81,1655,1511],{"class":127},[81,1657,1658],{"class":87}," StaleWhileRevalidate",[81,1660,1221],{"class":137},[81,1662,1664,1666,1669],{"class":83,"line":1663},33,[81,1665,1522],{"class":137},[81,1667,1668],{"class":91},"\"static-assets\"",[81,1670,44],{"class":137},[81,1672,1674],{"class":83,"line":1673},34,[81,1675,1533],{"class":137},[81,1677,1679],{"class":83,"line":1678},35,[81,1680,184],{"class":137},[81,1682,1684],{"class":83,"line":1683},36,[81,1685,151],{"emptyLinePlaceholder":150},[81,1687,1689],{"class":83,"line":1688},37,[81,1690,1691],{"class":768},"\u002F\u002F Register the route handling static assets\n",[81,1693,1695,1697],{"class":83,"line":1694},38,[81,1696,1555],{"class":87},[81,1698,1699],{"class":137},"(staticAssetsRoute);\n",[11,1701,1702],{},"Now once this service worker is activated the navigation requests should start\nin parallel to service worker boot rather than waiting for service worker to\nboot up first and thus fixing the slight delay.",[11,1704,1705,1706,1710],{},"There are many more workbox modules that can be integrated into our service\nworker. You can check out\n",[15,1707,1709],{"href":705,"rel":1708},[19],"Workbox Modules"," for a\nwhole list of amazing workbox modules.",[29,1712,1714],{"id":1713},"alternate-way-to-install-workbox-sw-pwa-in-vite","Alternate Way to install Workbox SW \u002F PWA in Vite",[11,1716,1717,1718,1720,1721,1726,1727,1732],{},"Instead of using ",[41,1719,383],{}," workbox command we can also use a custom library\ncalled ",[15,1722,1725],{"href":1723,"rel":1724},"https:\u002F\u002Fvite-pwa-org.netlify.app",[19],"PWA Vite Plugin"," that handles most of\nthe things regarding PWA \u002F installing Service Workers for us, assuming your\nproject uses Vite for build step. I highly recommend this if you just want to\nadd a SW and forget about it since this plugin requires almost zero-config to\nsetup. It comes with great integration for\n",[15,1728,1731],{"href":1729,"rel":1730},"https:\u002F\u002Fvite-pwa-org.netlify.app\u002Fframeworks\u002Fastro.html",[19],"Astro"," as well.",[11,1734,1735],{},"Further Reading \u002F References:",[1737,1738,1739,1745],"ul",{},[1740,1741,1742],"li",{},[15,1743,26],{"href":24,"rel":1744},[19],[1740,1746,1747],{},[15,1748,1750],{"href":17,"rel":1749},[19],"Service Worker MDN",[1752,1753,1754],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":77,"searchDepth":147,"depth":147,"links":1756},[1757,1758,1759],{"id":31,"depth":147,"text":32},{"id":650,"depth":147,"text":651},{"id":1713,"depth":147,"text":1714},"2023-02-17","Progressively enhance your sites performance by using Workbox Service Worker",false,"md",{},"\u002Fblog\u002Fworkbox-astro-project","8 min read",{"title":6,"description":1761},"blog\u002Fworkbox-astro-project","rFQgnyenh7w-26zzsRDHO-4qu8a-ZoXtj81DHZ8zY_s",{"id":1771,"title":1772,"body":1773,"date":3557,"description":3558,"draft":1762,"extension":1763,"meta":3559,"navigation":150,"path":3560,"readingTime":1766,"seo":3561,"stem":3562,"__hash__":3563},"posts\u002Fblog\u002Fnginx-brotli-gzip-compression-docker.md","Add Brotli and Gzip compression to nginx in Docker",{"type":8,"value":1774,"toc":3549},[1775,1787,1802,1806,1823,1846,1852,1926,1931,1997,2027,2031,2037,2059,2067,2308,2316,2421,2451,2454,2475,2502,2508,2528,2532,2548,2607,2610,2632,2654,2658,2667,2701,2802,2805,2821,2837,2842,3239,3243,3507,3515,3517,3546],[11,1776,1777,1782,1783,1786],{},[15,1778,1781],{"href":1779,"rel":1780},"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fbrotli",[19],"Brotli"," provides improved compression ratio\nand performance compared to gzip and can greatly improve webpage loading\nperformance since we are sending lot smaller bytes down the wire. Web browser\nsupport for brotli is pretty good too, almost all latest browsers support brotli\nfile format decompression. We can verify it by looking at ",[41,1784,1785],{},"Accept-Encoding: br","\nrequest header.",[11,1788,1789,1790,1795,1796,1801],{},"nginx comes with\n",[15,1791,1794],{"href":1792,"rel":1793},"https:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002Fhttp\u002Fngx_http_gzip_module.html",[19],"gzip"," module support\nby default (we need to turn it on \u002F opt in) but to setup brotli support we need\nto reconfigure nginx modules. I'll be using ",[15,1797,1800],{"href":1798,"rel":1799},"https:\u002F\u002Fwww.docker.com\u002F",[19],"Docker"," to\nsetup nginx with brotli compression but overall steps should be somewhat similar\nif you're setting it up manually in a server.",[29,1803,1805],{"id":1804},"dockerfile","Dockerfile",[11,1807,1808,1809,1812,1813,1818,1819,1822],{},"Let's create a simple ",[41,1810,1811],{},"DockerFile"," and use the latest stable\n",[15,1814,1817],{"href":1815,"rel":1816},"https:\u002F\u002Fhub.docker.com\u002F_\u002Fnginx\u002F",[19],"nginx docker image"," and set the working\ndirectory to ",[41,1820,1821],{},"\u002Froot\u002F"," (this can be whatever you want)",[72,1824,1828],{"className":1825,"code":1826,"language":1827,"meta":77,"style":77},"language-docker shiki shiki-themes github-dark","FROM nginx:1.23.2-alpine\nWORKDIR \u002Froot\u002F\n","docker",[41,1829,1830,1838],{"__ignoreMap":77},[81,1831,1832,1835],{"class":83,"line":84},[81,1833,1834],{"class":127},"FROM",[81,1836,1837],{"class":137}," nginx:1.23.2-alpine\n",[81,1839,1840,1843],{"class":83,"line":147},[81,1841,1842],{"class":127},"WORKDIR",[81,1844,1845],{"class":137}," \u002Froot\u002F\n",[11,1847,1848,1849,952],{},"Add a ",[41,1850,1851],{},"RUN",[72,1853,1855],{"className":1825,"code":1854,"language":1827,"meta":77,"style":77},"FROM nginx:1.23.2-alpine\nWORKDIR \u002Froot\u002F\n\nRUN apk add --update --no-cache git pcre-dev openssl-dev zlib-dev linux-headers build-base \\\n    && wget http:\u002F\u002Fnginx.org\u002Fdownload\u002Fnginx-1.23.2.tar.gz \\\n    && tar zxf nginx-1.23.2.tar.gz \\\n    && git clone https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli.git \\\n    && cd ngx_brotli \\\n    && git submodule update --init --recursive \\\n    && cd ..\u002Fnginx-1.23.2 \\\n    && .\u002Fconfigure --add-dynamic-module=..\u002Fngx_brotli --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-Os -fomit-frame-pointer -g' --with-ld-opt=-Wl,--as-needed,-O1,--sort-common \\\n    && make modules\n",[41,1856,1857,1863,1869,1873,1880,1885,1890,1895,1900,1905,1910,1921],{"__ignoreMap":77},[81,1858,1859,1861],{"class":83,"line":84},[81,1860,1834],{"class":127},[81,1862,1837],{"class":137},[81,1864,1865,1867],{"class":83,"line":147},[81,1866,1842],{"class":127},[81,1868,1845],{"class":137},[81,1870,1871],{"class":83,"line":154},[81,1872,151],{"emptyLinePlaceholder":150},[81,1874,1875,1877],{"class":83,"line":169},[81,1876,1851],{"class":127},[81,1878,1879],{"class":137}," apk add --update --no-cache git pcre-dev openssl-dev zlib-dev linux-headers build-base \\\n",[81,1881,1882],{"class":83,"line":187},[81,1883,1884],{"class":137},"    && wget http:\u002F\u002Fnginx.org\u002Fdownload\u002Fnginx-1.23.2.tar.gz \\\n",[81,1886,1887],{"class":83,"line":195},[81,1888,1889],{"class":137},"    && tar zxf nginx-1.23.2.tar.gz \\\n",[81,1891,1892],{"class":83,"line":201},[81,1893,1894],{"class":137},"    && git clone https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli.git \\\n",[81,1896,1897],{"class":83,"line":206},[81,1898,1899],{"class":137},"    && cd ngx_brotli \\\n",[81,1901,1902],{"class":83,"line":223},[81,1903,1904],{"class":137},"    && git submodule update --init --recursive \\\n",[81,1906,1907],{"class":83,"line":229},[81,1908,1909],{"class":137},"    && cd ..\u002Fnginx-1.23.2 \\\n",[81,1911,1912,1915,1918],{"class":83,"line":235},[81,1913,1914],{"class":137},"    && .\u002Fconfigure --add-dynamic-module=..\u002Fngx_brotli --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt=",[81,1916,1917],{"class":91},"'-Os -fomit-frame-pointer -g'",[81,1919,1920],{"class":137}," --with-ld-opt=-Wl,--as-needed,-O1,--sort-common \\\n",[81,1922,1923],{"class":83,"line":243},[81,1924,1925],{"class":137},"    && make modules\n",[1927,1928,1930],"h3",{"id":1929},"run-command-does-these-things","RUN command does these things:",[1932,1933,1934,1937,1940,1949,1978],"ol",{},[1740,1935,1936],{},"Update the packages, install git and other dependencies",[1740,1938,1939],{},"Download the nginx package (same version as our docker image) and unzip it in\nthe current working directory",[1740,1941,1942,1943,1948],{},"Clone the ",[15,1944,1947],{"href":1945,"rel":1946},"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli",[19],"ngx_brotli"," repository from\ngithub and run update submodule to fetch all data from the project",[1740,1950,1951,1952,1955,1956],{},"cd back into nginx package that we downloaded and run ",[41,1953,1954],{},"configure"," script\nwith:\n",[1737,1957,1958,1971],{},[1740,1959,1960,1961,1964,1965],{},"Default Arguments (",[41,1962,1963],{},"nginx -V"," to get the default compile arguments)\n",[400,1966],{"alt":1967,"dataZoomable":77,"height":1968,"loading":404,"src":1969,"width":1970},"nginx configure default arguments",582,"\u002Fcontent\u002Fnginx_default_arguments.png",1933,[1740,1972,1973,1974,1977],{},"Add Dynamic Module argument pointing to our downloaded ngx_brotli\nrepository (",[41,1975,1976],{},"..\u002Fngx_brotli",")",[1740,1979,1980,1985,1986,1989,1990,1993,1994],{},[15,1981,1984],{"href":1982,"rel":1983},"https:\u002F\u002Fwww.nginx.com\u002Fresources\u002Fwiki\u002Fextending\u002Fcompiling\u002F",[19],"Compile the dynamic modules","\nusing ",[41,1987,1988],{},"make modules"," which can be loaded using ",[41,1991,1992],{},"load_module"," in our\n",[41,1995,1996],{},"nginx.conf",[11,1998,1999,2000,2003,2004,2006,2007,2009,2010,933,2013,933,2016,44,2019,2022,2023,2026],{},"Note that we are combining\u002Fchaining multiple commands here using ",[41,2001,2002],{},"&&"," into a\nsingle ",[41,2005,1851],{}," command because docker executes each instruction in a new\nstandalone container\u002Flayer (except ",[41,2008,1834],{},"). We want our ",[41,2011,2012],{},"cd",[41,2014,2015],{},"wget",[41,2017,2018],{},"make",[41,2020,2021],{},".\u002Fconfigure"," etc commands to run in the same context under same directory\n(",[41,2024,2025],{},"PWD","), there is a workaround for this and it would require us to manually add\nabsolute paths to our commands but I find this to be much easier and it also\nreduces number of layers and size of our resulting docker image.",[29,2028,2030],{"id":2029},"load-brotli-module-and-enable-it","Load Brotli Module and Enable it",[11,2032,2033,2034,2036],{},"Now in our ",[41,2035,1996],{}," file we load the brotli module.",[72,2038,2042],{"className":2039,"code":2040,"language":2041,"meta":77,"style":77},"language-nginx shiki shiki-themes github-dark","load_module \u002Froot\u002Fnginx-1.23.2\u002Fobjs\u002Fngx_http_brotli_filter_module.so;\nload_module \u002Froot\u002Fnginx-1.23.2\u002Fobjs\u002Fngx_http_brotli_static_module.so;\n","nginx",[41,2043,2044,2052],{"__ignoreMap":77},[81,2045,2046,2049],{"class":83,"line":84},[81,2047,2048],{"class":127},"load_module ",[81,2050,2051],{"class":137},"\u002Froot\u002Fnginx-1.23.2\u002Fobjs\u002Fngx_http_brotli_filter_module.so;\n",[81,2053,2054,2056],{"class":83,"line":147},[81,2055,2048],{"class":127},[81,2057,2058],{"class":137},"\u002Froot\u002Fnginx-1.23.2\u002Fobjs\u002Fngx_http_brotli_static_module.so;\n",[11,2060,2061,2062,115],{},"We can enable the brotli module and\n",[15,2063,2066],{"href":2064,"rel":2065},"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli#configuration-directives",[19],"configure it",[72,2068,2070],{"className":2039,"code":2069,"language":2041,"meta":77,"style":77},"load_module \u002Froot\u002Fnginx-1.23.2\u002Fobjs\u002Fngx_http_brotli_filter_module.so;\nload_module \u002Froot\u002Fnginx-1.23.2\u002Fobjs\u002Fngx_http_brotli_static_module.so;\n\nuser nginx;\n\nworker_processes 1;\n\nevents {\n  worker_connections 1024;\n}\n\nhttp {\n  include \u002Fetc\u002Fnginx\u002Fmime.types;\n  default_type application\u002Foctet-stream;\n\n  root \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n\n  server {\n    listen 80;\n\n    sendfile on;\n    tcp_nopush on;\n    tcp_nodelay on;\n\n    # Add Brotli compression config\n    brotli on;\n    brotli_comp_level 6;\n    brotli_static on;\n    brotli_types application\u002Fatom+xml application\u002Fjavascript application\u002Fjson application\u002Frss+xml\n             application\u002Fvnd.ms-fontobject application\u002Fx-font-opentype application\u002Fx-font-truetype\n             application\u002Fx-font-ttf application\u002Fx-javascript application\u002Fxhtml+xml application\u002Fxml\n             font\u002Feot font\u002Fopentype font\u002Fotf font\u002Ftruetype image\u002Fsvg+xml image\u002Fvnd.microsoft.icon\n             image\u002Fx-icon image\u002Fx-win-bitmap text\u002Fcss text\u002Fjavascript text\u002Fplain text\u002Fxml text\u002Fhtml;\n  }\n\n}\n",[41,2071,2072,2078,2084,2088,2096,2100,2110,2114,2121,2131,2135,2139,2146,2154,2162,2166,2174,2178,2185,2195,2199,2209,2218,2227,2231,2236,2246,2256,2265,2273,2281,2286,2291,2296,2300,2304],{"__ignoreMap":77},[81,2073,2074,2076],{"class":83,"line":84},[81,2075,2048],{"class":127},[81,2077,2051],{"class":137},[81,2079,2080,2082],{"class":83,"line":147},[81,2081,2048],{"class":127},[81,2083,2058],{"class":137},[81,2085,2086],{"class":83,"line":154},[81,2087,151],{"emptyLinePlaceholder":150},[81,2089,2090,2093],{"class":83,"line":169},[81,2091,2092],{"class":127},"user ",[81,2094,2095],{"class":137},"nginx;\n",[81,2097,2098],{"class":83,"line":187},[81,2099,151],{"emptyLinePlaceholder":150},[81,2101,2102,2105,2108],{"class":83,"line":195},[81,2103,2104],{"class":127},"worker_processes ",[81,2106,2107],{"class":95},"1",[81,2109,144],{"class":137},[81,2111,2112],{"class":83,"line":201},[81,2113,151],{"emptyLinePlaceholder":150},[81,2115,2116,2119],{"class":83,"line":206},[81,2117,2118],{"class":127},"events",[81,2120,220],{"class":137},[81,2122,2123,2126,2129],{"class":83,"line":223},[81,2124,2125],{"class":127},"  worker_connections ",[81,2127,2128],{"class":95},"1024",[81,2130,144],{"class":137},[81,2132,2133],{"class":83,"line":229},[81,2134,198],{"class":137},[81,2136,2137],{"class":83,"line":235},[81,2138,151],{"emptyLinePlaceholder":150},[81,2140,2141,2144],{"class":83,"line":243},[81,2142,2143],{"class":127},"http",[81,2145,220],{"class":137},[81,2147,2148,2151],{"class":83,"line":249},[81,2149,2150],{"class":127},"  include ",[81,2152,2153],{"class":137},"\u002Fetc\u002Fnginx\u002Fmime.types;\n",[81,2155,2156,2159],{"class":83,"line":266},[81,2157,2158],{"class":127},"  default_type ",[81,2160,2161],{"class":137},"application\u002Foctet-stream;\n",[81,2163,2164],{"class":83,"line":590},[81,2165,151],{"emptyLinePlaceholder":150},[81,2167,2168,2171],{"class":83,"line":596},[81,2169,2170],{"class":127},"  root ",[81,2172,2173],{"class":137},"\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n",[81,2175,2176],{"class":83,"line":606},[81,2177,151],{"emptyLinePlaceholder":150},[81,2179,2180,2183],{"class":83,"line":1519},[81,2181,2182],{"class":127},"  server",[81,2184,220],{"class":137},[81,2186,2187,2190,2193],{"class":83,"line":1530},[81,2188,2189],{"class":127},"    listen ",[81,2191,2192],{"class":95},"80",[81,2194,144],{"class":137},[81,2196,2197],{"class":83,"line":1536},[81,2198,151],{"emptyLinePlaceholder":150},[81,2200,2201,2204,2207],{"class":83,"line":1541},[81,2202,2203],{"class":127},"    sendfile ",[81,2205,2206],{"class":95},"on",[81,2208,144],{"class":137},[81,2210,2211,2214,2216],{"class":83,"line":1546},[81,2212,2213],{"class":127},"    tcp_nopush ",[81,2215,2206],{"class":95},[81,2217,144],{"class":137},[81,2219,2220,2223,2225],{"class":83,"line":1552},[81,2221,2222],{"class":127},"    tcp_nodelay ",[81,2224,2206],{"class":95},[81,2226,144],{"class":137},[81,2228,2229],{"class":83,"line":1561},[81,2230,151],{"emptyLinePlaceholder":150},[81,2232,2233],{"class":83,"line":1566},[81,2234,2235],{"class":768},"    # Add Brotli compression config\n",[81,2237,2238,2241,2244],{"class":83,"line":1572},[81,2239,2240],{"class":127},"    brotli",[81,2242,2243],{"class":95}," on",[81,2245,144],{"class":137},[81,2247,2248,2251,2254],{"class":83,"line":1578},[81,2249,2250],{"class":127},"    brotli_comp_level",[81,2252,2253],{"class":95}," 6",[81,2255,144],{"class":137},[81,2257,2258,2261,2263],{"class":83,"line":1584},[81,2259,2260],{"class":127},"    brotli_static",[81,2262,2243],{"class":95},[81,2264,144],{"class":137},[81,2266,2267,2270],{"class":83,"line":1601},[81,2268,2269],{"class":127},"    brotli_types",[81,2271,2272],{"class":137}," application\u002Fatom+xml application\u002Fjavascript application\u002Fjson application\u002Frss+xml\n",[81,2274,2275,2278],{"class":83,"line":1617},[81,2276,2277],{"class":95},"             application\u002Fvnd.ms-fontobject",[81,2279,2280],{"class":137}," application\u002Fx-font-opentype application\u002Fx-font-truetype\n",[81,2282,2283],{"class":83,"line":1648},[81,2284,2285],{"class":137},"             application\u002Fx-font-ttf application\u002Fx-javascript application\u002Fxhtml+xml application\u002Fxml\n",[81,2287,2288],{"class":83,"line":1653},[81,2289,2290],{"class":137},"             font\u002Feot font\u002Fopentype font\u002Fotf font\u002Ftruetype image\u002Fsvg+xml image\u002Fvnd.microsoft.icon\n",[81,2292,2293],{"class":83,"line":1663},[81,2294,2295],{"class":137},"             image\u002Fx-icon image\u002Fx-win-bitmap text\u002Fcss text\u002Fjavascript text\u002Fplain text\u002Fxml text\u002Fhtml;\n",[81,2297,2298],{"class":83,"line":1673},[81,2299,363],{"class":137},[81,2301,2302],{"class":83,"line":1678},[81,2303,151],{"emptyLinePlaceholder":150},[81,2305,2306],{"class":83,"line":1683},[81,2307,198],{"class":137},[11,2309,2310,2312,2313],{},[41,2311,1805],{}," till now that sets up brotli compression and serves static files\nfrom ",[41,2314,2315],{},"\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F",[72,2317,2319],{"className":1825,"code":2318,"language":1827,"meta":77,"style":77},"FROM nginx:1.23.2-alpine AS builder\nWORKDIR \u002Froot\u002F\n\nRUN apk add --update --no-cache git pcre-dev openssl-dev zlib-dev linux-headers build-base \\\n    && wget http:\u002F\u002Fnginx.org\u002Fdownload\u002Fnginx-1.23.2.tar.gz \\\n    && tar zxf nginx-1.23.2.tar.gz \\\n    && git clone https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli.git \\\n    && cd ngx_brotli \\\n    && git submodule update --init --recursive \\\n    && cd ..\u002Fnginx-1.23.2 \\\n    && .\u002Fconfigure --add-dynamic-module=..\u002Fngx_brotli --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-Os -fomit-frame-pointer -g' --with-ld-opt=-Wl,--as-needed,-O1,--sort-common \\\n    && make modules\n\n# Updated nginx.conf file with load_module for brotli\nCOPY nginx.conf \u002Fetc\u002Fnginx\u002Fnginx.conf\n\nWORKDIR \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F\nCOPY www .\n",[41,2320,2321,2334,2340,2344,2350,2354,2358,2362,2366,2370,2374,2382,2386,2390,2395,2403,2407,2414],{"__ignoreMap":77},[81,2322,2323,2325,2328,2331],{"class":83,"line":84},[81,2324,1834],{"class":127},[81,2326,2327],{"class":137}," nginx:1.23.2-alpine ",[81,2329,2330],{"class":127},"AS",[81,2332,2333],{"class":137}," builder\n",[81,2335,2336,2338],{"class":83,"line":147},[81,2337,1842],{"class":127},[81,2339,1845],{"class":137},[81,2341,2342],{"class":83,"line":154},[81,2343,151],{"emptyLinePlaceholder":150},[81,2345,2346,2348],{"class":83,"line":169},[81,2347,1851],{"class":127},[81,2349,1879],{"class":137},[81,2351,2352],{"class":83,"line":187},[81,2353,1884],{"class":137},[81,2355,2356],{"class":83,"line":195},[81,2357,1889],{"class":137},[81,2359,2360],{"class":83,"line":201},[81,2361,1894],{"class":137},[81,2363,2364],{"class":83,"line":206},[81,2365,1899],{"class":137},[81,2367,2368],{"class":83,"line":223},[81,2369,1904],{"class":137},[81,2371,2372],{"class":83,"line":229},[81,2373,1909],{"class":137},[81,2375,2376,2378,2380],{"class":83,"line":235},[81,2377,1914],{"class":137},[81,2379,1917],{"class":91},[81,2381,1920],{"class":137},[81,2383,2384],{"class":83,"line":243},[81,2385,1925],{"class":137},[81,2387,2388],{"class":83,"line":249},[81,2389,151],{"emptyLinePlaceholder":150},[81,2391,2392],{"class":83,"line":266},[81,2393,2394],{"class":768},"# Updated nginx.conf file with load_module for brotli\n",[81,2396,2397,2400],{"class":83,"line":590},[81,2398,2399],{"class":127},"COPY",[81,2401,2402],{"class":137}," nginx.conf \u002Fetc\u002Fnginx\u002Fnginx.conf\n",[81,2404,2405],{"class":83,"line":596},[81,2406,151],{"emptyLinePlaceholder":150},[81,2408,2409,2411],{"class":83,"line":606},[81,2410,1842],{"class":127},[81,2412,2413],{"class":137}," \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F\n",[81,2415,2416,2418],{"class":83,"line":1519},[81,2417,2399],{"class":127},[81,2419,2420],{"class":137}," www .\n",[11,2422,2423,2424,2427,2428,2431,2432,2435,2436,2439,2440,2442,2443,2446,2447,2450],{},"I have made a ",[41,2425,2426],{},"www"," directory and added ",[41,2429,2430],{},"index.html"," and a ",[41,2433,2434],{},"styles.css"," file to\ntest brotli compression. If you see the ",[41,2437,2438],{},"brotli_types"," in the ",[41,2441,1996],{}," file,\nwe have ",[41,2444,2445],{},"text\u002Fcss"," and ",[41,2448,2449],{},"text\u002Fhtml"," added as the potential file types that we\nwould like to be compressed.",[11,2452,2453],{},"Let's test it out by building and starting the container.",[72,2455,2457],{"className":74,"code":2456,"language":76,"meta":77,"style":77},"docker build -t nginx-gzip-brotli .\n",[41,2458,2459],{"__ignoreMap":77},[81,2460,2461,2463,2466,2469,2472],{"class":83,"line":84},[81,2462,1827],{"class":87},[81,2464,2465],{"class":91}," build",[81,2467,2468],{"class":95}," -t",[81,2470,2471],{"class":91}," nginx-gzip-brotli",[81,2473,2474],{"class":91}," .\n",[72,2476,2478],{"className":74,"code":2477,"language":76,"meta":77,"style":77},"docker run --rm -itd -p 3000:80 nginx-gzip-brotli\n",[41,2479,2480],{"__ignoreMap":77},[81,2481,2482,2484,2487,2490,2493,2496,2499],{"class":83,"line":84},[81,2483,1827],{"class":87},[81,2485,2486],{"class":91}," run",[81,2488,2489],{"class":95}," --rm",[81,2491,2492],{"class":95}," -itd",[81,2494,2495],{"class":95}," -p",[81,2497,2498],{"class":91}," 3000:80",[81,2500,2501],{"class":91}," nginx-gzip-brotli\n",[11,2503,2504,2505,2507],{},"Make sure the browser supports brotli compression, i.e sends\n",[41,2506,1785],{}," header",[11,2509,2510,2516,2517,2523],{},[400,2511],{"alt":2512,"dataZoomable":77,"height":2513,"loading":404,"src":2514,"width":2515},"Browser Supports Brotli using Accept-Encoding Header",409,"\u002Fcontent\u002Fbrowser_support.png",1148,"\nNeat! We get the response in brotli format:\n",[400,2518],{"alt":2519,"dataZoomable":77,"height":2520,"loading":404,"src":2521,"width":2522},"Brotli Compression headers in browser",1172,"\u002Fcontent\u002Fbrotli_compression.png",2696,[400,2524],{"alt":2519,"dataZoomable":77,"height":2525,"loading":404,"src":2526,"width":2527},642,"\u002Fcontent\u002Fbrotli_compression_zoomed.png",1233,[29,2529,2531],{"id":2530},"add-fallback-gzip-compression","Add fallback Gzip compression",[11,2533,2534,2535,2538,2539,2541,2542,2547],{},"Now let's also add gzip compression to nginx so browsers that don't yet support\nbrotli format can use gzip. Since its support is built into nginx (I mean nginx\ncompiled with ",[41,2536,2537],{},"--with-http_gunzip_module --with-http_gzip_static_module"," flags,\nwhich is usually the case) we just have to update our ",[41,2540,1996],{}," file based on\n",[15,2543,2546],{"href":2544,"rel":2545},"https:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002Fhttp\u002Fngx_http_gzip_module.html#example",[19],"nginx_gzip_module","\nconfiguration parameters.",[72,2549,2551],{"className":2039,"code":2550,"language":2041,"meta":77,"style":77},"gzip on;\ngzip_vary on;\ngzip_min_length 20;\ngzip_types text\u002Fplain application\u002Fjson application\u002Fjavascript application\u002Fx-javascript text\u002Fjavascript text\u002Fcss;\ngzip_proxied expired no-cache no-store private auth;\ngzip_disable \"MSIE [1-6]\\.\";\n",[41,2552,2553,2562,2571,2581,2589,2597],{"__ignoreMap":77},[81,2554,2555,2558,2560],{"class":83,"line":84},[81,2556,2557],{"class":127},"gzip ",[81,2559,2206],{"class":95},[81,2561,144],{"class":137},[81,2563,2564,2567,2569],{"class":83,"line":147},[81,2565,2566],{"class":127},"gzip_vary ",[81,2568,2206],{"class":95},[81,2570,144],{"class":137},[81,2572,2573,2576,2579],{"class":83,"line":154},[81,2574,2575],{"class":127},"gzip_min_length ",[81,2577,2578],{"class":95},"20",[81,2580,144],{"class":137},[81,2582,2583,2586],{"class":83,"line":169},[81,2584,2585],{"class":127},"gzip_types ",[81,2587,2588],{"class":137},"text\u002Fplain application\u002Fjson application\u002Fjavascript application\u002Fx-javascript text\u002Fjavascript text\u002Fcss;\n",[81,2590,2591,2594],{"class":83,"line":187},[81,2592,2593],{"class":127},"gzip_proxied ",[81,2595,2596],{"class":137},"expired no-cache no-store private auth;\n",[81,2598,2599,2602,2605],{"class":83,"line":195},[81,2600,2601],{"class":127},"gzip_disable ",[81,2603,2604],{"class":91},"\"MSIE [1-6]\\.\"",[81,2606,144],{"class":137},[11,2608,2609],{},"Let's test it out if the fallback works with:",[72,2611,2613],{"className":74,"code":2612,"language":76,"meta":77,"style":77},"curl -I -H \"Accept-Encoding: gzip\" localhost:3333\n",[41,2614,2615],{"__ignoreMap":77},[81,2616,2617,2620,2623,2626,2629],{"class":83,"line":84},[81,2618,2619],{"class":87},"curl",[81,2621,2622],{"class":95}," -I",[81,2624,2625],{"class":95}," -H",[81,2627,2628],{"class":91}," \"Accept-Encoding: gzip\"",[81,2630,2631],{"class":91}," localhost:3333\n",[11,2633,2634,2640,2641,2644,2645,2647,2648],{},[400,2635],{"alt":2636,"dataZoomable":77,"height":2637,"loading":404,"src":2638,"width":2639},"Curl Command with Response Headers for gzip",433,"\u002Fcontent\u002Fcurl_gzip.png",1774,"\nAs we can see the response header ",[41,2642,2643],{},"Content-Encoding"," value as ",[41,2646,1794],{}," indicating response\ntype is compressed with gzip. Let's also check if brotli works when gzip and br headers\nare sent:\n",[400,2649],{"alt":2650,"dataZoomable":77,"height":2651,"loading":404,"src":2652,"width":2653},"Curl Command with Response Headers for gzip and br",420,"\u002Fcontent\u002Fcurl_gzip_br.png",1782,[29,2655,2657],{"id":2656},"cleaning-up-dockerfile","Cleaning up Dockerfile",[11,2659,2660,2661,2663,2664,2666],{},"If you see the Dockerfile, its setup is tied to a specific version of nginx (i.e\n1.23.2) and if we want to change the version number we would have to update it\nin multiple lines in the ",[41,2662,1805],{}," as well as update the brotli module paths\nin our ",[41,2665,1996],{}," files. Let's try to clean all that up and improve this.",[11,2668,2669,2670,2678,2679,2682,2683,2685,2686,2694,2695,2697,2698,2700],{},"We'll use ",[15,2671,2674,2675],{"href":2672,"rel":2673},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Freference\u002Fbuilder\u002F#arg",[19],"Docker ",[41,2676,2677],{},"ARG","\nto specify the ",[41,2680,2681],{},"version"," number of nginx and use it across our ",[41,2684,1805],{},".\nAlso note that we need add multiple ARG instruction because of how\n",[15,2687,2690,2693],{"href":2688,"rel":2689},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Freference\u002Fbuilder\u002F#scope",[19],[41,2691,2692],{},"SCOPE"," works"," in\n",[41,2696,1805],{}," after every ",[41,2699,1834],{}," instruction.",[72,2702,2704],{"className":1825,"code":2703,"language":1827,"meta":77,"style":77},"ARG version=1.23.2\nFROM nginx:${version}-alpine\nARG version\n\nRUN apk add --update --no-cache git pcre-dev openssl-dev zlib-dev linux-headers build-base \\\n    && wget http:\u002F\u002Fnginx.org\u002Fdownload\u002Fnginx-${version}.tar.gz \\\n    && tar zxf nginx-${version}.tar.gz \\\n    && git clone https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli.git \\\n    && cd ngx_brotli \\\n    && git submodule update --init --recursive \\\n    && cd ..\u002Fnginx-${version} \\\n    && .\u002Fconfigure --add-dynamic-module=..\u002Fngx_brotli --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-Os -fomit-frame-pointer -g' --with-ld-opt=-Wl,--as-needed,-O1,--sort-common \\\n    && make modules\n\nCOPY nginx.conf \u002Fetc\u002Fnginx\u002Fnginx.conf\n\nWORKDIR \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F\nCOPY www .\n",[41,2705,2706,2713,2720,2727,2731,2737,2742,2747,2751,2755,2759,2764,2772,2776,2780,2786,2790,2796],{"__ignoreMap":77},[81,2707,2708,2710],{"class":83,"line":84},[81,2709,2677],{"class":127},[81,2711,2712],{"class":137}," version=1.23.2\n",[81,2714,2715,2717],{"class":83,"line":147},[81,2716,1834],{"class":127},[81,2718,2719],{"class":137}," nginx:${version}-alpine\n",[81,2721,2722,2724],{"class":83,"line":154},[81,2723,2677],{"class":127},[81,2725,2726],{"class":137}," version\n",[81,2728,2729],{"class":83,"line":169},[81,2730,151],{"emptyLinePlaceholder":150},[81,2732,2733,2735],{"class":83,"line":187},[81,2734,1851],{"class":127},[81,2736,1879],{"class":137},[81,2738,2739],{"class":83,"line":195},[81,2740,2741],{"class":137},"    && wget http:\u002F\u002Fnginx.org\u002Fdownload\u002Fnginx-${version}.tar.gz \\\n",[81,2743,2744],{"class":83,"line":201},[81,2745,2746],{"class":137},"    && tar zxf nginx-${version}.tar.gz \\\n",[81,2748,2749],{"class":83,"line":206},[81,2750,1894],{"class":137},[81,2752,2753],{"class":83,"line":223},[81,2754,1899],{"class":137},[81,2756,2757],{"class":83,"line":229},[81,2758,1904],{"class":137},[81,2760,2761],{"class":83,"line":235},[81,2762,2763],{"class":137},"    && cd ..\u002Fnginx-${version} \\\n",[81,2765,2766,2768,2770],{"class":83,"line":243},[81,2767,1914],{"class":137},[81,2769,1917],{"class":91},[81,2771,1920],{"class":137},[81,2773,2774],{"class":83,"line":249},[81,2775,1925],{"class":137},[81,2777,2778],{"class":83,"line":266},[81,2779,151],{"emptyLinePlaceholder":150},[81,2781,2782,2784],{"class":83,"line":590},[81,2783,2399],{"class":127},[81,2785,2402],{"class":137},[81,2787,2788],{"class":83,"line":596},[81,2789,151],{"emptyLinePlaceholder":150},[81,2791,2792,2794],{"class":83,"line":606},[81,2793,1842],{"class":127},[81,2795,2413],{"class":137},[81,2797,2798,2800],{"class":83,"line":1519},[81,2799,2399],{"class":127},[81,2801,2420],{"class":137},[11,2803,2804],{},"Okay now let's try to clean-up our nginx.conf file and free it from nginx\nversion number. i.e these lines",[72,2806,2807],{"className":2039,"code":2040,"language":2041,"meta":77,"style":77},[41,2808,2809,2815],{"__ignoreMap":77},[81,2810,2811,2813],{"class":83,"line":84},[81,2812,2048],{"class":127},[81,2814,2051],{"class":137},[81,2816,2817,2819],{"class":83,"line":147},[81,2818,2048],{"class":127},[81,2820,2058],{"class":137},[11,2822,2823,2824,2827,2828,2830,2831,2833,2834,2836],{},"We can copy the compiled dynamic modules from our ",[41,2825,2826],{},"builder"," image into\n\u002Fusr\u002Flib\u002Fnginx\u002Fmodules directory in a clean ",[41,2829,2041],{}," image as a new layer and\nload those in ",[41,2832,1996],{}," file. Note: I have also updated the Dockerfile\n",[41,2835,2021],{}," command and added few extra parameters there for this to work\nproperly.",[11,2838,2839,2840,115],{},"Complete ",[41,2841,1811],{},[72,2843,2845],{"className":1825,"code":2844,"language":1827,"meta":77,"style":77},"ARG version=1.23.2\nFROM nginx:${version}-alpine AS builder\nARG version\n\nWORKDIR \u002Froot\u002F\n\nRUN apk add --update --no-cache git pcre-dev openssl-dev zlib-dev linux-headers build-base \\\n    && wget http:\u002F\u002Fnginx.org\u002Fdownload\u002Fnginx-${version}.tar.gz \\\n    && tar zxf nginx-${version}.tar.gz \\\n    && git clone https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli.git \\\n    && cd ngx_brotli \\\n    && git submodule update --init --recursive \\\n    && cd ..\u002Fnginx-${version} \\\n    && .\u002Fconfigure \\\n    --add-dynamic-module=..\u002Fngx_brotli \\\n    --prefix=\u002Fetc\u002Fnginx \\\n    --sbin-path=\u002Fusr\u002Fsbin\u002Fnginx \\\n    --modules-path=\u002Fusr\u002Flib\u002Fnginx\u002Fmodules \\\n    --conf-path=\u002Fetc\u002Fnginx\u002Fnginx.conf \\\n    --error-log-path=\u002Fvar\u002Flog\u002Fnginx\u002Ferror.log \\\n    --http-log-path=\u002Fvar\u002Flog\u002Fnginx\u002Faccess.log \\\n    --pid-path=\u002Fvar\u002Frun\u002Fnginx.pid \\\n    --lock-path=\u002Fvar\u002Frun\u002Fnginx.lock \\\n    --http-client-body-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fclient_temp \\\n    --http-proxy-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fproxy_temp \\\n    --http-fastcgi-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Ffastcgi_temp \\\n    --http-uwsgi-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fuwsgi_temp \\\n    --http-scgi-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fscgi_temp \\\n    --with-perl_modules_path=\u002Fusr\u002Flib\u002Fperl5\u002Fvendor_perl \\\n    --user=nginx \\\n    --group=nginx \\\n    --with-compat \\\n    --with-file-aio \\\n    --with-threads \\\n    --with-http_addition_module \\\n    --with-http_auth_request_module \\\n    --with-http_dav_module \\\n    --with-http_flv_module \\\n    --with-http_gunzip_module \\\n    --with-http_gzip_static_module \\\n    --with-http_mp4_module \\\n    --with-http_random_index_module \\\n    --with-http_realip_module \\\n    --with-http_secure_link_module \\\n    --with-http_slice_module \\\n    --with-http_ssl_module \\\n    --with-http_stub_status_module \\\n    --with-http_sub_module \\\n    --with-http_v2_module \\\n    --with-mail \\\n    --with-mail_ssl_module \\\n    --with-stream \\\n    --with-stream_realip_module \\\n    --with-stream_ssl_module \\\n    --with-stream_ssl_preread_module \\\n    --with-cc-opt='-Os -fomit-frame-pointer -g' \\\n    --with-ld-opt=-Wl,--as-needed,-O1,--sort-common \\\n    && make modules\n\nFROM nginx:${version}-alpine\n\nARG version\n\nCOPY --from=builder \u002Froot\u002Fnginx-${version}\u002Fobjs\u002Fngx_http_brotli_filter_module.so \u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002F\nCOPY --from=builder \u002Froot\u002Fnginx-${version}\u002Fobjs\u002Fngx_http_brotli_static_module.so \u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002F\n\nCOPY nginx.conf \u002Fetc\u002Fnginx\u002Fnginx.conf\n\nWORKDIR \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F\nCOPY www .\n",[41,2846,2847,2853,2864,2870,2874,2880,2884,2890,2894,2898,2902,2906,2910,2914,2919,2924,2929,2934,2939,2944,2949,2954,2959,2964,2969,2974,2979,2984,2989,2994,2999,3004,3009,3014,3019,3024,3029,3034,3039,3045,3051,3057,3063,3069,3075,3081,3087,3093,3099,3105,3111,3117,3123,3129,3135,3141,3152,3158,3163,3168,3175,3180,3187,3192,3200,3208,3213,3220,3225,3232],{"__ignoreMap":77},[81,2848,2849,2851],{"class":83,"line":84},[81,2850,2677],{"class":127},[81,2852,2712],{"class":137},[81,2854,2855,2857,2860,2862],{"class":83,"line":147},[81,2856,1834],{"class":127},[81,2858,2859],{"class":137}," nginx:${version}-alpine ",[81,2861,2330],{"class":127},[81,2863,2333],{"class":137},[81,2865,2866,2868],{"class":83,"line":154},[81,2867,2677],{"class":127},[81,2869,2726],{"class":137},[81,2871,2872],{"class":83,"line":169},[81,2873,151],{"emptyLinePlaceholder":150},[81,2875,2876,2878],{"class":83,"line":187},[81,2877,1842],{"class":127},[81,2879,1845],{"class":137},[81,2881,2882],{"class":83,"line":195},[81,2883,151],{"emptyLinePlaceholder":150},[81,2885,2886,2888],{"class":83,"line":201},[81,2887,1851],{"class":127},[81,2889,1879],{"class":137},[81,2891,2892],{"class":83,"line":206},[81,2893,2741],{"class":137},[81,2895,2896],{"class":83,"line":223},[81,2897,2746],{"class":137},[81,2899,2900],{"class":83,"line":229},[81,2901,1894],{"class":137},[81,2903,2904],{"class":83,"line":235},[81,2905,1899],{"class":137},[81,2907,2908],{"class":83,"line":243},[81,2909,1904],{"class":137},[81,2911,2912],{"class":83,"line":249},[81,2913,2763],{"class":137},[81,2915,2916],{"class":83,"line":266},[81,2917,2918],{"class":137},"    && .\u002Fconfigure \\\n",[81,2920,2921],{"class":83,"line":590},[81,2922,2923],{"class":137},"    --add-dynamic-module=..\u002Fngx_brotli \\\n",[81,2925,2926],{"class":83,"line":596},[81,2927,2928],{"class":137},"    --prefix=\u002Fetc\u002Fnginx \\\n",[81,2930,2931],{"class":83,"line":606},[81,2932,2933],{"class":137},"    --sbin-path=\u002Fusr\u002Fsbin\u002Fnginx \\\n",[81,2935,2936],{"class":83,"line":1519},[81,2937,2938],{"class":137},"    --modules-path=\u002Fusr\u002Flib\u002Fnginx\u002Fmodules \\\n",[81,2940,2941],{"class":83,"line":1530},[81,2942,2943],{"class":137},"    --conf-path=\u002Fetc\u002Fnginx\u002Fnginx.conf \\\n",[81,2945,2946],{"class":83,"line":1536},[81,2947,2948],{"class":137},"    --error-log-path=\u002Fvar\u002Flog\u002Fnginx\u002Ferror.log \\\n",[81,2950,2951],{"class":83,"line":1541},[81,2952,2953],{"class":137},"    --http-log-path=\u002Fvar\u002Flog\u002Fnginx\u002Faccess.log \\\n",[81,2955,2956],{"class":83,"line":1546},[81,2957,2958],{"class":137},"    --pid-path=\u002Fvar\u002Frun\u002Fnginx.pid \\\n",[81,2960,2961],{"class":83,"line":1552},[81,2962,2963],{"class":137},"    --lock-path=\u002Fvar\u002Frun\u002Fnginx.lock \\\n",[81,2965,2966],{"class":83,"line":1561},[81,2967,2968],{"class":137},"    --http-client-body-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fclient_temp \\\n",[81,2970,2971],{"class":83,"line":1566},[81,2972,2973],{"class":137},"    --http-proxy-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fproxy_temp \\\n",[81,2975,2976],{"class":83,"line":1572},[81,2977,2978],{"class":137},"    --http-fastcgi-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Ffastcgi_temp \\\n",[81,2980,2981],{"class":83,"line":1578},[81,2982,2983],{"class":137},"    --http-uwsgi-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fuwsgi_temp \\\n",[81,2985,2986],{"class":83,"line":1584},[81,2987,2988],{"class":137},"    --http-scgi-temp-path=\u002Fvar\u002Fcache\u002Fnginx\u002Fscgi_temp \\\n",[81,2990,2991],{"class":83,"line":1601},[81,2992,2993],{"class":137},"    --with-perl_modules_path=\u002Fusr\u002Flib\u002Fperl5\u002Fvendor_perl \\\n",[81,2995,2996],{"class":83,"line":1617},[81,2997,2998],{"class":137},"    --user=nginx \\\n",[81,3000,3001],{"class":83,"line":1648},[81,3002,3003],{"class":137},"    --group=nginx \\\n",[81,3005,3006],{"class":83,"line":1653},[81,3007,3008],{"class":137},"    --with-compat \\\n",[81,3010,3011],{"class":83,"line":1663},[81,3012,3013],{"class":137},"    --with-file-aio \\\n",[81,3015,3016],{"class":83,"line":1673},[81,3017,3018],{"class":137},"    --with-threads \\\n",[81,3020,3021],{"class":83,"line":1678},[81,3022,3023],{"class":137},"    --with-http_addition_module \\\n",[81,3025,3026],{"class":83,"line":1683},[81,3027,3028],{"class":137},"    --with-http_auth_request_module \\\n",[81,3030,3031],{"class":83,"line":1688},[81,3032,3033],{"class":137},"    --with-http_dav_module \\\n",[81,3035,3036],{"class":83,"line":1694},[81,3037,3038],{"class":137},"    --with-http_flv_module \\\n",[81,3040,3042],{"class":83,"line":3041},39,[81,3043,3044],{"class":137},"    --with-http_gunzip_module \\\n",[81,3046,3048],{"class":83,"line":3047},40,[81,3049,3050],{"class":137},"    --with-http_gzip_static_module \\\n",[81,3052,3054],{"class":83,"line":3053},41,[81,3055,3056],{"class":137},"    --with-http_mp4_module \\\n",[81,3058,3060],{"class":83,"line":3059},42,[81,3061,3062],{"class":137},"    --with-http_random_index_module \\\n",[81,3064,3066],{"class":83,"line":3065},43,[81,3067,3068],{"class":137},"    --with-http_realip_module \\\n",[81,3070,3072],{"class":83,"line":3071},44,[81,3073,3074],{"class":137},"    --with-http_secure_link_module \\\n",[81,3076,3078],{"class":83,"line":3077},45,[81,3079,3080],{"class":137},"    --with-http_slice_module \\\n",[81,3082,3084],{"class":83,"line":3083},46,[81,3085,3086],{"class":137},"    --with-http_ssl_module \\\n",[81,3088,3090],{"class":83,"line":3089},47,[81,3091,3092],{"class":137},"    --with-http_stub_status_module \\\n",[81,3094,3096],{"class":83,"line":3095},48,[81,3097,3098],{"class":137},"    --with-http_sub_module \\\n",[81,3100,3102],{"class":83,"line":3101},49,[81,3103,3104],{"class":137},"    --with-http_v2_module \\\n",[81,3106,3108],{"class":83,"line":3107},50,[81,3109,3110],{"class":137},"    --with-mail \\\n",[81,3112,3114],{"class":83,"line":3113},51,[81,3115,3116],{"class":137},"    --with-mail_ssl_module \\\n",[81,3118,3120],{"class":83,"line":3119},52,[81,3121,3122],{"class":137},"    --with-stream \\\n",[81,3124,3126],{"class":83,"line":3125},53,[81,3127,3128],{"class":137},"    --with-stream_realip_module \\\n",[81,3130,3132],{"class":83,"line":3131},54,[81,3133,3134],{"class":137},"    --with-stream_ssl_module \\\n",[81,3136,3138],{"class":83,"line":3137},55,[81,3139,3140],{"class":137},"    --with-stream_ssl_preread_module \\\n",[81,3142,3144,3147,3149],{"class":83,"line":3143},56,[81,3145,3146],{"class":137},"    --with-cc-opt=",[81,3148,1917],{"class":91},[81,3150,3151],{"class":137}," \\\n",[81,3153,3155],{"class":83,"line":3154},57,[81,3156,3157],{"class":137},"    --with-ld-opt=-Wl,--as-needed,-O1,--sort-common \\\n",[81,3159,3161],{"class":83,"line":3160},58,[81,3162,1925],{"class":137},[81,3164,3166],{"class":83,"line":3165},59,[81,3167,151],{"emptyLinePlaceholder":150},[81,3169,3171,3173],{"class":83,"line":3170},60,[81,3172,1834],{"class":127},[81,3174,2719],{"class":137},[81,3176,3178],{"class":83,"line":3177},61,[81,3179,151],{"emptyLinePlaceholder":150},[81,3181,3183,3185],{"class":83,"line":3182},62,[81,3184,2677],{"class":127},[81,3186,2726],{"class":137},[81,3188,3190],{"class":83,"line":3189},63,[81,3191,151],{"emptyLinePlaceholder":150},[81,3193,3195,3197],{"class":83,"line":3194},64,[81,3196,2399],{"class":127},[81,3198,3199],{"class":137}," --from=builder \u002Froot\u002Fnginx-${version}\u002Fobjs\u002Fngx_http_brotli_filter_module.so \u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002F\n",[81,3201,3203,3205],{"class":83,"line":3202},65,[81,3204,2399],{"class":127},[81,3206,3207],{"class":137}," --from=builder \u002Froot\u002Fnginx-${version}\u002Fobjs\u002Fngx_http_brotli_static_module.so \u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002F\n",[81,3209,3211],{"class":83,"line":3210},66,[81,3212,151],{"emptyLinePlaceholder":150},[81,3214,3216,3218],{"class":83,"line":3215},67,[81,3217,2399],{"class":127},[81,3219,2402],{"class":137},[81,3221,3223],{"class":83,"line":3222},68,[81,3224,151],{"emptyLinePlaceholder":150},[81,3226,3228,3230],{"class":83,"line":3227},69,[81,3229,1842],{"class":127},[81,3231,2413],{"class":137},[81,3233,3235,3237],{"class":83,"line":3234},70,[81,3236,2399],{"class":127},[81,3238,2420],{"class":137},[11,3240,2839,3241,115],{},[41,3242,1996],{},[72,3244,3246],{"className":2039,"code":3245,"language":2041,"meta":77,"style":77},"load_module \u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002Fngx_http_brotli_filter_module.so;\nload_module \u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002Fngx_http_brotli_static_module.so;\n\nuser nginx;\n\nworker_processes 1;\n\nevents {\n  worker_connections 1024;\n}\n\nhttp {\n  include \u002Fetc\u002Fnginx\u002Fmime.types;\n  default_type application\u002Foctet-stream;\n\n  root \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n\n  server {\n    listen 80;\n\n    sendfile on;\n    tcp_nopush on;\n    tcp_nodelay on;\n\n    gzip on;\n    gzip_vary on;\n    gzip_min_length 20;\n    gzip_types text\u002Fplain application\u002Fjson application\u002Fjavascript application\u002Fx-javascript text\u002Fjavascript text\u002Fcss;\n    gzip_proxied expired no-cache no-store private auth;\n    gzip_disable \"MSIE [1-6]\\.\";\n\n    # Add Brotli compression config\n    brotli on;\n    brotli_comp_level 6;\n    brotli_static on;\n    brotli_types application\u002Fatom+xml application\u002Fjavascript application\u002Fjson application\u002Frss+xml\n             application\u002Fvnd.ms-fontobject application\u002Fx-font-opentype application\u002Fx-font-truetype\n             application\u002Fx-font-ttf application\u002Fx-javascript application\u002Fxhtml+xml application\u002Fxml\n             font\u002Feot font\u002Fopentype font\u002Fotf font\u002Ftruetype image\u002Fsvg+xml image\u002Fvnd.microsoft.icon\n             image\u002Fx-icon image\u002Fx-win-bitmap text\u002Fcss text\u002Fjavascript text\u002Fplain text\u002Fxml;\n  }\n\n}\n",[41,3247,3248,3255,3262,3266,3272,3276,3284,3288,3294,3302,3306,3310,3316,3322,3328,3332,3338,3342,3348,3356,3360,3368,3376,3384,3388,3397,3406,3415,3422,3429,3438,3442,3446,3454,3462,3470,3476,3482,3486,3490,3495,3499,3503],{"__ignoreMap":77},[81,3249,3250,3252],{"class":83,"line":84},[81,3251,2048],{"class":127},[81,3253,3254],{"class":137},"\u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002Fngx_http_brotli_filter_module.so;\n",[81,3256,3257,3259],{"class":83,"line":147},[81,3258,2048],{"class":127},[81,3260,3261],{"class":137},"\u002Fusr\u002Flib\u002Fnginx\u002Fmodules\u002Fngx_http_brotli_static_module.so;\n",[81,3263,3264],{"class":83,"line":154},[81,3265,151],{"emptyLinePlaceholder":150},[81,3267,3268,3270],{"class":83,"line":169},[81,3269,2092],{"class":127},[81,3271,2095],{"class":137},[81,3273,3274],{"class":83,"line":187},[81,3275,151],{"emptyLinePlaceholder":150},[81,3277,3278,3280,3282],{"class":83,"line":195},[81,3279,2104],{"class":127},[81,3281,2107],{"class":95},[81,3283,144],{"class":137},[81,3285,3286],{"class":83,"line":201},[81,3287,151],{"emptyLinePlaceholder":150},[81,3289,3290,3292],{"class":83,"line":206},[81,3291,2118],{"class":127},[81,3293,220],{"class":137},[81,3295,3296,3298,3300],{"class":83,"line":223},[81,3297,2125],{"class":127},[81,3299,2128],{"class":95},[81,3301,144],{"class":137},[81,3303,3304],{"class":83,"line":229},[81,3305,198],{"class":137},[81,3307,3308],{"class":83,"line":235},[81,3309,151],{"emptyLinePlaceholder":150},[81,3311,3312,3314],{"class":83,"line":243},[81,3313,2143],{"class":127},[81,3315,220],{"class":137},[81,3317,3318,3320],{"class":83,"line":249},[81,3319,2150],{"class":127},[81,3321,2153],{"class":137},[81,3323,3324,3326],{"class":83,"line":266},[81,3325,2158],{"class":127},[81,3327,2161],{"class":137},[81,3329,3330],{"class":83,"line":590},[81,3331,151],{"emptyLinePlaceholder":150},[81,3333,3334,3336],{"class":83,"line":596},[81,3335,2170],{"class":127},[81,3337,2173],{"class":137},[81,3339,3340],{"class":83,"line":606},[81,3341,151],{"emptyLinePlaceholder":150},[81,3343,3344,3346],{"class":83,"line":1519},[81,3345,2182],{"class":127},[81,3347,220],{"class":137},[81,3349,3350,3352,3354],{"class":83,"line":1530},[81,3351,2189],{"class":127},[81,3353,2192],{"class":95},[81,3355,144],{"class":137},[81,3357,3358],{"class":83,"line":1536},[81,3359,151],{"emptyLinePlaceholder":150},[81,3361,3362,3364,3366],{"class":83,"line":1541},[81,3363,2203],{"class":127},[81,3365,2206],{"class":95},[81,3367,144],{"class":137},[81,3369,3370,3372,3374],{"class":83,"line":1546},[81,3371,2213],{"class":127},[81,3373,2206],{"class":95},[81,3375,144],{"class":137},[81,3377,3378,3380,3382],{"class":83,"line":1552},[81,3379,2222],{"class":127},[81,3381,2206],{"class":95},[81,3383,144],{"class":137},[81,3385,3386],{"class":83,"line":1561},[81,3387,151],{"emptyLinePlaceholder":150},[81,3389,3390,3393,3395],{"class":83,"line":1566},[81,3391,3392],{"class":127},"    gzip ",[81,3394,2206],{"class":95},[81,3396,144],{"class":137},[81,3398,3399,3402,3404],{"class":83,"line":1572},[81,3400,3401],{"class":127},"    gzip_vary ",[81,3403,2206],{"class":95},[81,3405,144],{"class":137},[81,3407,3408,3411,3413],{"class":83,"line":1578},[81,3409,3410],{"class":127},"    gzip_min_length ",[81,3412,2578],{"class":95},[81,3414,144],{"class":137},[81,3416,3417,3420],{"class":83,"line":1584},[81,3418,3419],{"class":127},"    gzip_types ",[81,3421,2588],{"class":137},[81,3423,3424,3427],{"class":83,"line":1601},[81,3425,3426],{"class":127},"    gzip_proxied ",[81,3428,2596],{"class":137},[81,3430,3431,3434,3436],{"class":83,"line":1617},[81,3432,3433],{"class":127},"    gzip_disable ",[81,3435,2604],{"class":91},[81,3437,144],{"class":137},[81,3439,3440],{"class":83,"line":1648},[81,3441,151],{"emptyLinePlaceholder":150},[81,3443,3444],{"class":83,"line":1653},[81,3445,2235],{"class":768},[81,3447,3448,3450,3452],{"class":83,"line":1663},[81,3449,2240],{"class":127},[81,3451,2243],{"class":95},[81,3453,144],{"class":137},[81,3455,3456,3458,3460],{"class":83,"line":1673},[81,3457,2250],{"class":127},[81,3459,2253],{"class":95},[81,3461,144],{"class":137},[81,3463,3464,3466,3468],{"class":83,"line":1678},[81,3465,2260],{"class":127},[81,3467,2243],{"class":95},[81,3469,144],{"class":137},[81,3471,3472,3474],{"class":83,"line":1683},[81,3473,2269],{"class":127},[81,3475,2272],{"class":137},[81,3477,3478,3480],{"class":83,"line":1688},[81,3479,2277],{"class":95},[81,3481,2280],{"class":137},[81,3483,3484],{"class":83,"line":1694},[81,3485,2285],{"class":137},[81,3487,3488],{"class":83,"line":3041},[81,3489,2290],{"class":137},[81,3491,3492],{"class":83,"line":3047},[81,3493,3494],{"class":137},"             image\u002Fx-icon image\u002Fx-win-bitmap text\u002Fcss text\u002Fjavascript text\u002Fplain text\u002Fxml;\n",[81,3496,3497],{"class":83,"line":3053},[81,3498,363],{"class":137},[81,3500,3501],{"class":83,"line":3059},[81,3502,151],{"emptyLinePlaceholder":150},[81,3504,3505],{"class":83,"line":3065},[81,3506,198],{"class":137},[11,3508,3509,3510,3512,3513],{},"Now if we ever want to update our nginx version we just update one ARG ",[41,3511,2681],{},"\ninstruction in our ",[41,3514,1805],{},[11,3516,1735],{},[1737,3518,3519,3526,3533,3539],{},[1740,3520,3521],{},[15,3522,3525],{"href":3523,"rel":3524},"https:\u002F\u002Fcss-tricks.com\u002Fbrotli-static-compression\u002F",[19],"Brotli and Static Compression",[1740,3527,3528],{},[15,3529,3532],{"href":3530,"rel":3531},"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fngx_brotli#installation",[19],"nginx brotli",[1740,3534,3535],{},[15,3536,3538],{"href":2544,"rel":3537},[19],"nginx gzip",[1740,3540,3541],{},[15,3542,3545],{"href":3543,"rel":3544},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Freference\u002Fbuilder\u002F",[19],"Dockerfile Reference",[1752,3547,3548],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}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 pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":77,"searchDepth":147,"depth":147,"links":3550},[3551,3554,3555,3556],{"id":1804,"depth":147,"text":1805,"children":3552},[3553],{"id":1929,"depth":154,"text":1930},{"id":2029,"depth":147,"text":2030},{"id":2530,"depth":147,"text":2531},{"id":2656,"depth":147,"text":2657},"2022-11-27","Configure nginx with brotli and gzip compression running in a docker container",{},"\u002Fblog\u002Fnginx-brotli-gzip-compression-docker",{"title":1772,"description":3558},"blog\u002Fnginx-brotli-gzip-compression-docker","UqCQ4maIMoD6OwVrBp2FFh6IoSbzEjbpDCQAecSRCJU",{"id":3565,"title":3566,"body":3567,"date":7283,"description":7284,"draft":1762,"extension":1763,"meta":7285,"navigation":150,"path":7286,"readingTime":7287,"seo":7288,"stem":7289,"__hash__":7290},"posts\u002Fblog\u002Fopen-graph-images-using-satori.md","Dynamic OG images using Satori and Astro Server Endpoints",{"type":8,"value":3568,"toc":7274},[3569,3572,3591,3612,3616,3624,3641,3652,3660,3664,3676,3735,3740,3894,3907,4196,4211,4548,4554,4562,4572,4579,4583,4594,4619,4717,4726,4855,4861,4875,4879,4885,4986,4995,5009,5018,5027,5035,5046,5050,5060,5381,5384,5400,5404,5415,5429,5464,5472,5477,5551,5570,5575,5621,5638,6013,6016,6617,6627,6634,6638,6646,6653,6661,6668,6774,6785,6790,7223,7230,7240,7247,7249,7271],[11,3570,3571],{},"Generating Open Graph images would usually require a separate service or a\nserver with a headless browser that renders the layout HTML template (after\nloading custom fonts, images etc) and generate a PNG image of the page which\nwould then be sent to the client. This solution is inherently very slow and\nresource intensive.",[11,3573,3574,3575,3582,3583,3590],{},"To solve this problem Vercel has launched a package called\n",[15,3576,3579],{"href":3577,"rel":3578},"https:\u002F\u002Fvercel.com\u002Fdocs\u002Fconcepts\u002Ffunctions\u002Fedge-functions\u002Fog-image-generation",[19],[41,3580,3581],{},"@vercel\u002Fog","\nthat works seamlessly with Next.js and Vercel Edge Functions. Internally it uses\nanother package by vercel called ",[15,3584,3587],{"href":3585,"rel":3586},"https:\u002F\u002Fgithub.com\u002Fvercel\u002Fsatori",[19],[41,3588,3589],{},"satori","\nthat converts HTML and CSS into SVG and the best part about it is it supports\nJSX, TailwindCSS and custom fonts out of the box.",[11,3592,3593,3594,3599,3600,3607,3608,3611],{},"We can use this package in our\n",[15,3595,3598],{"href":3596,"rel":3597},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fcore-concepts\u002Fendpoints\u002F#server-endpoints-api-routes",[19],"Astro Server Endpoints","\nto generate Dynamic Open Graph Images. But Satori expects JSX as input and since\nwe cannot directly write JSX in our Astro Server Endpoints we'll use a package\ncalled ",[15,3601,3604],{"href":3602,"rel":3603},"https:\u002F\u002Fgithub.com\u002Fnatemoo-re\u002Fsatori-html",[19],[41,3605,3606],{},"satori-html"," by Nate Moore\nthat basically helps us write html in template strings and gives back Satori\ncompatible ",[41,3609,3610],{},"VNode"," that can be passed directly to Satori and generate our Open\nGraph image as SVG.",[29,3613,3615],{"id":3614},"initial-setup","Initial Setup",[11,3617,3618,3619,2446,3621,3623],{},"Let's add ",[41,3620,3589],{},[41,3622,3606],{}," to our Astro Project.",[72,3625,3627],{"className":74,"code":3626,"language":76,"meta":77,"style":77},"npm install satori satori-html\n",[41,3628,3629],{"__ignoreMap":77},[81,3630,3631,3633,3635,3638],{"class":83,"line":84},[81,3632,88],{"class":87},[81,3634,92],{"class":91},[81,3636,3637],{"class":91}," satori",[81,3639,3640],{"class":91}," satori-html\n",[11,3642,3643,3644,3647,3648,3651],{},"I'll create an ",[41,3645,3646],{},"index.og.ts"," file that corresponds to Astro Server API endpoint\n",[41,3649,3650],{},"\u002Findex.og"," in the Source Root Directory.",[11,3653,3654],{},[400,3655],{"alt":3656,"dataZoomable":77,"height":3657,"loading":404,"src":3658,"width":3659},"Initial Project Setup",692,"\u002Fcontent\u002Finitial_project_setup.png",522,[29,3661,3663],{"id":3662},"generate-open-graph-svg","Generate Open Graph SVG",[11,3665,3666,3667,3672,3673,3675],{},"Let's create a ",[15,3668,3671],{"href":3669,"rel":3670},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fguides\u002Fendpoints\u002F#server-endpoints-api-routes",[19],"Astro Server Endpoint"," GET route in the ",[41,3674,3646],{}," file that\nwould eventually return our dynamic open graph image.",[72,3677,3681],{"className":3678,"code":3679,"language":3680,"meta":77,"style":77},"language-ts shiki shiki-themes github-dark","import type { APIContext } from \"astro\";\n\nexport async function GET({ params }: APIContext) {}\n","ts",[41,3682,3683,3700,3704],{"__ignoreMap":77},[81,3684,3685,3687,3690,3693,3695,3698],{"class":83,"line":84},[81,3686,736],{"class":127},[81,3688,3689],{"class":127}," type",[81,3691,3692],{"class":137}," { APIContext } ",[81,3694,742],{"class":127},[81,3696,3697],{"class":91}," \"astro\"",[81,3699,144],{"class":137},[81,3701,3702],{"class":83,"line":147},[81,3703,151],{"emptyLinePlaceholder":150},[81,3705,3706,3709,3712,3715,3718,3721,3724,3727,3729,3732],{"class":83,"line":154},[81,3707,3708],{"class":127},"export",[81,3710,3711],{"class":127}," async",[81,3713,3714],{"class":127}," function",[81,3716,3717],{"class":87}," GET",[81,3719,3720],{"class":137},"({ ",[81,3722,3723],{"class":547},"params",[81,3725,3726],{"class":137}," }",[81,3728,115],{"class":127},[81,3730,3731],{"class":87}," APIContext",[81,3733,3734],{"class":137},") {}\n",[11,3736,3737,3738,212],{},"Now lets use write our html with inline-styles that would become the Open Graph\nimage and pass it to ",[41,3739,3606],{},[72,3741,3743],{"className":3678,"code":3742,"language":3680,"meta":77,"style":77},"import type { APIContext } from \"astro\";\nimport { html } from \"satori-html\";\n\nexport async function GET({ params }: APIContext) {\n  const markup = html(`\n  \u003Cdiv\n    style=\"height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgb(45,26,84); font-size: 32px; font-weight: 600;\"\n  >\n    \u003Cdiv\n      style=\"font-size: 70px; margin-top: 38px; display: flex; flex-direction: column; color: white;\"\n    >\n      \u003Cspan\n        >Hello from\n        \u003Cspan style=\"margin-left:15ch;color: rgb(255,93,1);\"\n          >Astro\u003C\u002Fspan\n        >\u003C\u002Fspan\n      >\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>`);\n};\n",[41,3744,3745,3759,3773,3777,3800,3818,3823,3828,3833,3838,3843,3848,3853,3858,3863,3868,3873,3878,3883,3890],{"__ignoreMap":77},[81,3746,3747,3749,3751,3753,3755,3757],{"class":83,"line":84},[81,3748,736],{"class":127},[81,3750,3689],{"class":127},[81,3752,3692],{"class":137},[81,3754,742],{"class":127},[81,3756,3697],{"class":91},[81,3758,144],{"class":137},[81,3760,3761,3763,3766,3768,3771],{"class":83,"line":147},[81,3762,736],{"class":127},[81,3764,3765],{"class":137}," { html } ",[81,3767,742],{"class":127},[81,3769,3770],{"class":91}," \"satori-html\"",[81,3772,144],{"class":137},[81,3774,3775],{"class":83,"line":154},[81,3776,151],{"emptyLinePlaceholder":150},[81,3778,3779,3781,3783,3785,3787,3789,3791,3793,3795,3797],{"class":83,"line":169},[81,3780,3708],{"class":127},[81,3782,3711],{"class":127},[81,3784,3714],{"class":127},[81,3786,3717],{"class":87},[81,3788,3720],{"class":137},[81,3790,3723],{"class":547},[81,3792,3726],{"class":137},[81,3794,115],{"class":127},[81,3796,3731],{"class":87},[81,3798,3799],{"class":137},") {\n",[81,3801,3802,3805,3808,3810,3813,3815],{"class":83,"line":187},[81,3803,3804],{"class":127},"  const",[81,3806,3807],{"class":95}," markup",[81,3809,134],{"class":127},[81,3811,3812],{"class":87}," html",[81,3814,178],{"class":137},[81,3816,3817],{"class":91},"`\n",[81,3819,3820],{"class":83,"line":195},[81,3821,3822],{"class":91},"  \u003Cdiv\n",[81,3824,3825],{"class":83,"line":201},[81,3826,3827],{"class":91},"    style=\"height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgb(45,26,84); font-size: 32px; font-weight: 600;\"\n",[81,3829,3830],{"class":83,"line":206},[81,3831,3832],{"class":91},"  >\n",[81,3834,3835],{"class":83,"line":223},[81,3836,3837],{"class":91},"    \u003Cdiv\n",[81,3839,3840],{"class":83,"line":229},[81,3841,3842],{"class":91},"      style=\"font-size: 70px; margin-top: 38px; display: flex; flex-direction: column; color: white;\"\n",[81,3844,3845],{"class":83,"line":235},[81,3846,3847],{"class":91},"    >\n",[81,3849,3850],{"class":83,"line":243},[81,3851,3852],{"class":91},"      \u003Cspan\n",[81,3854,3855],{"class":83,"line":249},[81,3856,3857],{"class":91},"        >Hello from\n",[81,3859,3860],{"class":83,"line":266},[81,3861,3862],{"class":91},"        \u003Cspan style=\"margin-left:15ch;color: rgb(255,93,1);\"\n",[81,3864,3865],{"class":83,"line":590},[81,3866,3867],{"class":91},"          >Astro\u003C\u002Fspan\n",[81,3869,3870],{"class":83,"line":596},[81,3871,3872],{"class":91},"        >\u003C\u002Fspan\n",[81,3874,3875],{"class":83,"line":606},[81,3876,3877],{"class":91},"      >\n",[81,3879,3880],{"class":83,"line":1519},[81,3881,3882],{"class":91},"    \u003C\u002Fdiv>\n",[81,3884,3885,3888],{"class":83,"line":1530},[81,3886,3887],{"class":91},"  \u003C\u002Fdiv>`",[81,3889,184],{"class":137},[81,3891,3892],{"class":83,"line":1536},[81,3893,269],{"class":137},[11,3895,3896,3897,3900,3901,3904,3905,212],{},"I have a custom font file saved in ",[41,3898,3899],{},"\u002Fpublic\u002Ffonts\u002F"," directory. We'll use\n",[41,3902,3903],{},"readFileSync"," method from standard Node.js API to load the custom font file and\ngenerate our SVG using ",[41,3906,3589],{},[72,3908,3910],{"className":3678,"code":3909,"language":3680,"meta":77,"style":77},"import type { APIContext } from \"astro\";\nimport { html } from \"satori-html\";\nimport { readFileSync } from \"fs\";\n\nexport async function GET({ params }: APIContext) {\n  const markup = html(`\n  \u003Cdiv\n    style=\"height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgb(45,26,84); font-size: 32px; font-weight: 600;\"\n  >\n    \u003Cdiv\n      style=\"font-size: 70px; margin-top: 38px; display: flex; flex-direction: column; color: white;\"\n    >\n      \u003Cspan\n        >Hello from\n        \u003Cspan style=\"margin-left:15ch;color: rgb(255,93,1);\"\n          >Astro\u003C\u002Fspan\n        >\u003C\u002Fspan\n      >\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>`);\n\n  const fontFilePath = `${process.cwd()}\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf`;\n\n  const fontFile = readFileSync(fontFilePath);\n\n  const svg = await satori(markup, {\n    width: 1200,\n    height: 630,\n    fonts: [\n      {\n        name: \"Optimistic Display\",\n        data: fontFile,\n        style: \"normal\",\n      },\n    ],\n  });\n};\n",[41,3911,3912,3926,3938,3952,3956,3978,3992,3996,4000,4004,4008,4012,4016,4020,4024,4028,4032,4036,4040,4044,4050,4054,4082,4086,4101,4105,4122,4132,4142,4147,4152,4162,4167,4177,4182,4187,4192],{"__ignoreMap":77},[81,3913,3914,3916,3918,3920,3922,3924],{"class":83,"line":84},[81,3915,736],{"class":127},[81,3917,3689],{"class":127},[81,3919,3692],{"class":137},[81,3921,742],{"class":127},[81,3923,3697],{"class":91},[81,3925,144],{"class":137},[81,3927,3928,3930,3932,3934,3936],{"class":83,"line":147},[81,3929,736],{"class":127},[81,3931,3765],{"class":137},[81,3933,742],{"class":127},[81,3935,3770],{"class":91},[81,3937,144],{"class":137},[81,3939,3940,3942,3945,3947,3950],{"class":83,"line":154},[81,3941,736],{"class":127},[81,3943,3944],{"class":137}," { readFileSync } ",[81,3946,742],{"class":127},[81,3948,3949],{"class":91}," \"fs\"",[81,3951,144],{"class":137},[81,3953,3954],{"class":83,"line":169},[81,3955,151],{"emptyLinePlaceholder":150},[81,3957,3958,3960,3962,3964,3966,3968,3970,3972,3974,3976],{"class":83,"line":187},[81,3959,3708],{"class":127},[81,3961,3711],{"class":127},[81,3963,3714],{"class":127},[81,3965,3717],{"class":87},[81,3967,3720],{"class":137},[81,3969,3723],{"class":547},[81,3971,3726],{"class":137},[81,3973,115],{"class":127},[81,3975,3731],{"class":87},[81,3977,3799],{"class":137},[81,3979,3980,3982,3984,3986,3988,3990],{"class":83,"line":195},[81,3981,3804],{"class":127},[81,3983,3807],{"class":95},[81,3985,134],{"class":127},[81,3987,3812],{"class":87},[81,3989,178],{"class":137},[81,3991,3817],{"class":91},[81,3993,3994],{"class":83,"line":201},[81,3995,3822],{"class":91},[81,3997,3998],{"class":83,"line":206},[81,3999,3827],{"class":91},[81,4001,4002],{"class":83,"line":223},[81,4003,3832],{"class":91},[81,4005,4006],{"class":83,"line":229},[81,4007,3837],{"class":91},[81,4009,4010],{"class":83,"line":235},[81,4011,3842],{"class":91},[81,4013,4014],{"class":83,"line":243},[81,4015,3847],{"class":91},[81,4017,4018],{"class":83,"line":249},[81,4019,3852],{"class":91},[81,4021,4022],{"class":83,"line":266},[81,4023,3857],{"class":91},[81,4025,4026],{"class":83,"line":590},[81,4027,3862],{"class":91},[81,4029,4030],{"class":83,"line":596},[81,4031,3867],{"class":91},[81,4033,4034],{"class":83,"line":606},[81,4035,3872],{"class":91},[81,4037,4038],{"class":83,"line":1519},[81,4039,3877],{"class":91},[81,4041,4042],{"class":83,"line":1530},[81,4043,3882],{"class":91},[81,4045,4046,4048],{"class":83,"line":1536},[81,4047,3887],{"class":91},[81,4049,184],{"class":137},[81,4051,4052],{"class":83,"line":1541},[81,4053,151],{"emptyLinePlaceholder":150},[81,4055,4056,4058,4061,4063,4066,4069,4071,4074,4077,4080],{"class":83,"line":1546},[81,4057,3804],{"class":127},[81,4059,4060],{"class":95}," fontFilePath",[81,4062,134],{"class":127},[81,4064,4065],{"class":91}," `${",[81,4067,4068],{"class":137},"process",[81,4070,212],{"class":91},[81,4072,4073],{"class":87},"cwd",[81,4075,4076],{"class":91},"()",[81,4078,4079],{"class":91},"}\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf`",[81,4081,144],{"class":137},[81,4083,4084],{"class":83,"line":1552},[81,4085,151],{"emptyLinePlaceholder":150},[81,4087,4088,4090,4093,4095,4098],{"class":83,"line":1561},[81,4089,3804],{"class":127},[81,4091,4092],{"class":95}," fontFile",[81,4094,134],{"class":127},[81,4096,4097],{"class":87}," readFileSync",[81,4099,4100],{"class":137},"(fontFilePath);\n",[81,4102,4103],{"class":83,"line":1566},[81,4104,151],{"emptyLinePlaceholder":150},[81,4106,4107,4109,4112,4114,4117,4119],{"class":83,"line":1572},[81,4108,3804],{"class":127},[81,4110,4111],{"class":95}," svg",[81,4113,134],{"class":127},[81,4115,4116],{"class":127}," await",[81,4118,3637],{"class":87},[81,4120,4121],{"class":137},"(markup, {\n",[81,4123,4124,4127,4130],{"class":83,"line":1578},[81,4125,4126],{"class":137},"    width: ",[81,4128,4129],{"class":95},"1200",[81,4131,44],{"class":137},[81,4133,4134,4137,4140],{"class":83,"line":1584},[81,4135,4136],{"class":137},"    height: ",[81,4138,4139],{"class":95},"630",[81,4141,44],{"class":137},[81,4143,4144],{"class":83,"line":1601},[81,4145,4146],{"class":137},"    fonts: [\n",[81,4148,4149],{"class":83,"line":1617},[81,4150,4151],{"class":137},"      {\n",[81,4153,4154,4157,4160],{"class":83,"line":1648},[81,4155,4156],{"class":137},"        name: ",[81,4158,4159],{"class":91},"\"Optimistic Display\"",[81,4161,44],{"class":137},[81,4163,4164],{"class":83,"line":1653},[81,4165,4166],{"class":137},"        data: fontFile,\n",[81,4168,4169,4172,4175],{"class":83,"line":1663},[81,4170,4171],{"class":137},"        style: ",[81,4173,4174],{"class":91},"\"normal\"",[81,4176,44],{"class":137},[81,4178,4179],{"class":83,"line":1673},[81,4180,4181],{"class":137},"      },\n",[81,4183,4184],{"class":83,"line":1678},[81,4185,4186],{"class":137},"    ],\n",[81,4188,4189],{"class":83,"line":1683},[81,4190,4191],{"class":137},"  });\n",[81,4193,4194],{"class":83,"line":1688},[81,4195,269],{"class":137},[11,4197,4198,4199,4202,4203,4210],{},"Let's test if this works by returning the resulting ",[41,4200,4201],{},"svg"," from satori as text\n",[15,4204,4207],{"href":4205,"rel":4206},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FResponse",[19],[41,4208,4209],{},"Response"," from our\nGET API route.",[72,4212,4214],{"className":3678,"code":4213,"language":3680,"meta":77,"style":77},"import type { APIContext } from \"astro\";\nimport { html } from \"satori-html\";\nimport { readFileSync } from \"fs\";\nimport satori from \"satori\";\n\nexport async function GET({ params }: APIContext) {\n  const markup = html(`\n  \u003Cdiv\n    style=\"height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgb(45,26,84); font-size: 32px; font-weight: 600;\"\n  >\n    \u003Cdiv\n      style=\"font-size: 70px; margin-top: 38px; display: flex; flex-direction: column; color: white;\"\n    >\n      \u003Cspan\n        >Hello from\n        \u003Cspan style=\"margin-left:15ch;color: rgb(255,93,1);\"\n          >Astro\u003C\u002Fspan\n        >\u003C\u002Fspan\n      >\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>`);\n\n  const fontFilePath = `${process.cwd()}\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf`;\n\n  const fontFile = readFileSync(fontFilePath);\n\n  \u002F\u002F resulting svg is basically a long string\n  const svg: string = await satori(markup, {\n    width: 1200,\n    height: 630,\n    fonts: [\n      {\n        name: \"Optimistic Display\",\n        data: fontFile,\n        style: \"normal\",\n      },\n    ],\n  });\n\n  return new Response(svg, {\n    status: 200,\n    headers: {\n      \"Content-Type\": \"text\u002Fplain\",\n    },\n  });\n};\n",[41,4215,4216,4230,4242,4254,4268,4272,4294,4308,4312,4316,4320,4324,4328,4332,4336,4340,4344,4348,4352,4356,4360,4366,4370,4392,4396,4408,4412,4417,4436,4444,4452,4456,4460,4468,4472,4480,4484,4488,4492,4496,4508,4518,4523,4535,4540,4544],{"__ignoreMap":77},[81,4217,4218,4220,4222,4224,4226,4228],{"class":83,"line":84},[81,4219,736],{"class":127},[81,4221,3689],{"class":127},[81,4223,3692],{"class":137},[81,4225,742],{"class":127},[81,4227,3697],{"class":91},[81,4229,144],{"class":137},[81,4231,4232,4234,4236,4238,4240],{"class":83,"line":147},[81,4233,736],{"class":127},[81,4235,3765],{"class":137},[81,4237,742],{"class":127},[81,4239,3770],{"class":91},[81,4241,144],{"class":137},[81,4243,4244,4246,4248,4250,4252],{"class":83,"line":154},[81,4245,736],{"class":127},[81,4247,3944],{"class":137},[81,4249,742],{"class":127},[81,4251,3949],{"class":91},[81,4253,144],{"class":137},[81,4255,4256,4258,4261,4263,4266],{"class":83,"line":169},[81,4257,736],{"class":127},[81,4259,4260],{"class":137}," satori ",[81,4262,742],{"class":127},[81,4264,4265],{"class":91}," \"satori\"",[81,4267,144],{"class":137},[81,4269,4270],{"class":83,"line":187},[81,4271,151],{"emptyLinePlaceholder":150},[81,4273,4274,4276,4278,4280,4282,4284,4286,4288,4290,4292],{"class":83,"line":195},[81,4275,3708],{"class":127},[81,4277,3711],{"class":127},[81,4279,3714],{"class":127},[81,4281,3717],{"class":87},[81,4283,3720],{"class":137},[81,4285,3723],{"class":547},[81,4287,3726],{"class":137},[81,4289,115],{"class":127},[81,4291,3731],{"class":87},[81,4293,3799],{"class":137},[81,4295,4296,4298,4300,4302,4304,4306],{"class":83,"line":201},[81,4297,3804],{"class":127},[81,4299,3807],{"class":95},[81,4301,134],{"class":127},[81,4303,3812],{"class":87},[81,4305,178],{"class":137},[81,4307,3817],{"class":91},[81,4309,4310],{"class":83,"line":206},[81,4311,3822],{"class":91},[81,4313,4314],{"class":83,"line":223},[81,4315,3827],{"class":91},[81,4317,4318],{"class":83,"line":229},[81,4319,3832],{"class":91},[81,4321,4322],{"class":83,"line":235},[81,4323,3837],{"class":91},[81,4325,4326],{"class":83,"line":243},[81,4327,3842],{"class":91},[81,4329,4330],{"class":83,"line":249},[81,4331,3847],{"class":91},[81,4333,4334],{"class":83,"line":266},[81,4335,3852],{"class":91},[81,4337,4338],{"class":83,"line":590},[81,4339,3857],{"class":91},[81,4341,4342],{"class":83,"line":596},[81,4343,3862],{"class":91},[81,4345,4346],{"class":83,"line":606},[81,4347,3867],{"class":91},[81,4349,4350],{"class":83,"line":1519},[81,4351,3872],{"class":91},[81,4353,4354],{"class":83,"line":1530},[81,4355,3877],{"class":91},[81,4357,4358],{"class":83,"line":1536},[81,4359,3882],{"class":91},[81,4361,4362,4364],{"class":83,"line":1541},[81,4363,3887],{"class":91},[81,4365,184],{"class":137},[81,4367,4368],{"class":83,"line":1546},[81,4369,151],{"emptyLinePlaceholder":150},[81,4371,4372,4374,4376,4378,4380,4382,4384,4386,4388,4390],{"class":83,"line":1552},[81,4373,3804],{"class":127},[81,4375,4060],{"class":95},[81,4377,134],{"class":127},[81,4379,4065],{"class":91},[81,4381,4068],{"class":137},[81,4383,212],{"class":91},[81,4385,4073],{"class":87},[81,4387,4076],{"class":91},[81,4389,4079],{"class":91},[81,4391,144],{"class":137},[81,4393,4394],{"class":83,"line":1561},[81,4395,151],{"emptyLinePlaceholder":150},[81,4397,4398,4400,4402,4404,4406],{"class":83,"line":1566},[81,4399,3804],{"class":127},[81,4401,4092],{"class":95},[81,4403,134],{"class":127},[81,4405,4097],{"class":87},[81,4407,4100],{"class":137},[81,4409,4410],{"class":83,"line":1572},[81,4411,151],{"emptyLinePlaceholder":150},[81,4413,4414],{"class":83,"line":1578},[81,4415,4416],{"class":768},"  \u002F\u002F resulting svg is basically a long string\n",[81,4418,4419,4421,4423,4425,4428,4430,4432,4434],{"class":83,"line":1584},[81,4420,3804],{"class":127},[81,4422,4111],{"class":95},[81,4424,115],{"class":127},[81,4426,4427],{"class":95}," string",[81,4429,134],{"class":127},[81,4431,4116],{"class":127},[81,4433,3637],{"class":87},[81,4435,4121],{"class":137},[81,4437,4438,4440,4442],{"class":83,"line":1601},[81,4439,4126],{"class":137},[81,4441,4129],{"class":95},[81,4443,44],{"class":137},[81,4445,4446,4448,4450],{"class":83,"line":1617},[81,4447,4136],{"class":137},[81,4449,4139],{"class":95},[81,4451,44],{"class":137},[81,4453,4454],{"class":83,"line":1648},[81,4455,4146],{"class":137},[81,4457,4458],{"class":83,"line":1653},[81,4459,4151],{"class":137},[81,4461,4462,4464,4466],{"class":83,"line":1663},[81,4463,4156],{"class":137},[81,4465,4159],{"class":91},[81,4467,44],{"class":137},[81,4469,4470],{"class":83,"line":1673},[81,4471,4166],{"class":137},[81,4473,4474,4476,4478],{"class":83,"line":1678},[81,4475,4171],{"class":137},[81,4477,4174],{"class":91},[81,4479,44],{"class":137},[81,4481,4482],{"class":83,"line":1683},[81,4483,4181],{"class":137},[81,4485,4486],{"class":83,"line":1688},[81,4487,4186],{"class":137},[81,4489,4490],{"class":83,"line":1694},[81,4491,4191],{"class":137},[81,4493,4494],{"class":83,"line":3041},[81,4495,151],{"emptyLinePlaceholder":150},[81,4497,4498,4500,4502,4505],{"class":83,"line":3047},[81,4499,190],{"class":127},[81,4501,1500],{"class":127},[81,4503,4504],{"class":87}," Response",[81,4506,4507],{"class":137},"(svg, {\n",[81,4509,4510,4513,4516],{"class":83,"line":3053},[81,4511,4512],{"class":137},"    status: ",[81,4514,4515],{"class":95},"200",[81,4517,44],{"class":137},[81,4519,4520],{"class":83,"line":3059},[81,4521,4522],{"class":137},"    headers: {\n",[81,4524,4525,4528,4530,4533],{"class":83,"line":3065},[81,4526,4527],{"class":91},"      \"Content-Type\"",[81,4529,343],{"class":137},[81,4531,4532],{"class":91},"\"text\u002Fplain\"",[81,4534,44],{"class":137},[81,4536,4537],{"class":83,"line":3071},[81,4538,4539],{"class":137},"    },\n",[81,4541,4542],{"class":83,"line":3077},[81,4543,4191],{"class":137},[81,4545,4546],{"class":83,"line":3083},[81,4547,269],{"class":137},[11,4549,4550,4551,4553],{},"Visit ",[41,4552,3650],{}," route in the browser we get SVG as a plain text",[11,4555,4556],{},[400,4557],{"alt":4558,"dataZoomable":77,"height":4559,"loading":404,"src":4560,"width":4561},"SVG as plain text response from Astro Server Endpoint",994,"\u002Fcontent\u002Fsvg_response.png",1688,[11,4563,4564,4565,946,4568,4571],{},"Changing the ",[41,4566,4567],{},"Content-Type",[41,4569,4570],{},"\"image\u002Fsvg+xml\""," should let the browser know the\nresponse is of type svg and will render it instead of showing it as text.",[11,4573,4574],{},[400,4575],{"alt":4558,"dataZoomable":77,"height":4576,"loading":404,"src":4577,"width":4578},1217,"\u002Fcontent\u002Fsvg_as_img.png",2176,[29,4580,4582],{"id":4581},"create-png-from-svg","Create PNG from SVG",[11,4584,4585,4586,4593],{},"To convert SVG into a PNG file we can use\n",[15,4587,4590],{"href":4588,"rel":4589},"https:\u002F\u002Fsharp.pixelplumbing.com\u002F",[19],[41,4591,4592],{},"sharp"," library.",[72,4595,4597],{"className":74,"code":4596,"language":76,"meta":77,"style":77},"npm install sharp\nnpm install -D @types\u002Fsharp\n",[41,4598,4599,4608],{"__ignoreMap":77},[81,4600,4601,4603,4605],{"class":83,"line":84},[81,4602,88],{"class":87},[81,4604,92],{"class":91},[81,4606,4607],{"class":91}," sharp\n",[81,4609,4610,4612,4614,4616],{"class":83,"line":147},[81,4611,88],{"class":87},[81,4613,92],{"class":91},[81,4615,96],{"class":95},[81,4617,4618],{"class":91}," @types\u002Fsharp\n",[72,4620,4622],{"className":3678,"code":4621,"language":3680,"meta":77,"style":77},"import sharp from \"sharp\";\n\nexport async function GET({ params }: APIContext) {\n  \u002F\u002F ...\n  const png = sharp(Buffer.from(svg)).png();\n  const response = await png.toBuffer();\n};\n",[41,4623,4624,4638,4642,4664,4669,4694,4713],{"__ignoreMap":77},[81,4625,4626,4628,4631,4633,4636],{"class":83,"line":84},[81,4627,736],{"class":127},[81,4629,4630],{"class":137}," sharp ",[81,4632,742],{"class":127},[81,4634,4635],{"class":91}," \"sharp\"",[81,4637,144],{"class":137},[81,4639,4640],{"class":83,"line":147},[81,4641,151],{"emptyLinePlaceholder":150},[81,4643,4644,4646,4648,4650,4652,4654,4656,4658,4660,4662],{"class":83,"line":154},[81,4645,3708],{"class":127},[81,4647,3711],{"class":127},[81,4649,3714],{"class":127},[81,4651,3717],{"class":87},[81,4653,3720],{"class":137},[81,4655,3723],{"class":547},[81,4657,3726],{"class":137},[81,4659,115],{"class":127},[81,4661,3731],{"class":87},[81,4663,3799],{"class":137},[81,4665,4666],{"class":83,"line":169},[81,4667,4668],{"class":768},"  \u002F\u002F ...\n",[81,4670,4671,4673,4676,4678,4681,4684,4686,4689,4692],{"class":83,"line":187},[81,4672,3804],{"class":127},[81,4674,4675],{"class":95}," png",[81,4677,134],{"class":127},[81,4679,4680],{"class":87}," sharp",[81,4682,4683],{"class":137},"(Buffer.",[81,4685,742],{"class":87},[81,4687,4688],{"class":137},"(svg)).",[81,4690,4691],{"class":87},"png",[81,4693,759],{"class":137},[81,4695,4696,4698,4701,4703,4705,4708,4711],{"class":83,"line":195},[81,4697,3804],{"class":127},[81,4699,4700],{"class":95}," response",[81,4702,134],{"class":127},[81,4704,4116],{"class":127},[81,4706,4707],{"class":137}," png.",[81,4709,4710],{"class":87},"toBuffer",[81,4712,759],{"class":137},[81,4714,4715],{"class":83,"line":201},[81,4716,269],{"class":137},[11,4718,4719,4720,946,4722,4725],{},"Set the ",[41,4721,4567],{},[41,4723,4724],{},"image\u002Fpng"," and return the response",[72,4727,4729],{"className":3678,"code":4728,"language":3680,"meta":77,"style":77},"import sharp from \"sharp\";\n\nexport async function GET({ params }: APIContext) {\n  \u002F\u002F ...\n  const png = sharp(Buffer.from(svg)).png();\n  const response = await png.toBuffer();\n  return new Response(response, {\n    status: 200,\n    headers: {\n      \"Content-Type\": \"image\u002Fpng\",\n    },\n  });\n};\n",[41,4730,4731,4743,4747,4769,4773,4793,4809,4820,4828,4832,4843,4847,4851],{"__ignoreMap":77},[81,4732,4733,4735,4737,4739,4741],{"class":83,"line":84},[81,4734,736],{"class":127},[81,4736,4630],{"class":137},[81,4738,742],{"class":127},[81,4740,4635],{"class":91},[81,4742,144],{"class":137},[81,4744,4745],{"class":83,"line":147},[81,4746,151],{"emptyLinePlaceholder":150},[81,4748,4749,4751,4753,4755,4757,4759,4761,4763,4765,4767],{"class":83,"line":154},[81,4750,3708],{"class":127},[81,4752,3711],{"class":127},[81,4754,3714],{"class":127},[81,4756,3717],{"class":87},[81,4758,3720],{"class":137},[81,4760,3723],{"class":547},[81,4762,3726],{"class":137},[81,4764,115],{"class":127},[81,4766,3731],{"class":87},[81,4768,3799],{"class":137},[81,4770,4771],{"class":83,"line":169},[81,4772,4668],{"class":768},[81,4774,4775,4777,4779,4781,4783,4785,4787,4789,4791],{"class":83,"line":187},[81,4776,3804],{"class":127},[81,4778,4675],{"class":95},[81,4780,134],{"class":127},[81,4782,4680],{"class":87},[81,4784,4683],{"class":137},[81,4786,742],{"class":87},[81,4788,4688],{"class":137},[81,4790,4691],{"class":87},[81,4792,759],{"class":137},[81,4794,4795,4797,4799,4801,4803,4805,4807],{"class":83,"line":195},[81,4796,3804],{"class":127},[81,4798,4700],{"class":95},[81,4800,134],{"class":127},[81,4802,4116],{"class":127},[81,4804,4707],{"class":137},[81,4806,4710],{"class":87},[81,4808,759],{"class":137},[81,4810,4811,4813,4815,4817],{"class":83,"line":201},[81,4812,190],{"class":127},[81,4814,1500],{"class":127},[81,4816,4504],{"class":87},[81,4818,4819],{"class":137},"(response, {\n",[81,4821,4822,4824,4826],{"class":83,"line":206},[81,4823,4512],{"class":137},[81,4825,4515],{"class":95},[81,4827,44],{"class":137},[81,4829,4830],{"class":83,"line":223},[81,4831,4522],{"class":137},[81,4833,4834,4836,4838,4841],{"class":83,"line":229},[81,4835,4527],{"class":91},[81,4837,343],{"class":137},[81,4839,4840],{"class":91},"\"image\u002Fpng\"",[81,4842,44],{"class":137},[81,4844,4845],{"class":83,"line":235},[81,4846,4539],{"class":137},[81,4848,4849],{"class":83,"line":243},[81,4850,4191],{"class":137},[81,4852,4853],{"class":83,"line":249},[81,4854,269],{"class":137},[11,4856,4857,4858,4860],{},"If we check our ",[41,4859,3650],{}," route now, we get the response as a image",[11,4862,4863,4869],{},[400,4864],{"alt":4865,"dataZoomable":77,"height":4866,"loading":404,"src":4867,"width":4868},"SVG converted to Png using sharp",1716,"\u002Fcontent\u002Fpng_response.png",3078,[400,4870],{"alt":4871,"dataZoomable":77,"height":4872,"loading":404,"src":4873,"width":4874},"SVG converted to Png response type in browser console",834,"\u002Fcontent\u002Fpng_response_zoomed.png",1470,[29,4876,4878],{"id":4877},"deploy","Deploy",[11,4880,4881,4882,115],{},"Astro Server Endpoints work only in SSR mode. Let's enable deployment in SSR\nmode and add an adapter by modifying ",[41,4883,4884],{},"astro.config.mjs",[72,4886,4888],{"className":3678,"code":4887,"language":3680,"meta":77,"style":77},"import { defineConfig } from \"astro\u002Fconfig\";\n\n\u002F\u002F https:\u002F\u002Fastro.build\u002Fconfig\nimport vercel from \"@astrojs\u002Fvercel\u002Fserverless\";\n\n\u002F\u002F https:\u002F\u002Fastro.build\u002Fconfig\nexport default defineConfig({\n  output: \"server\",\n  adapter: vercel({\n    includeFiles: [\".\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf\"],\n  }),\n});\n",[41,4889,4890,4904,4908,4913,4927,4931,4935,4947,4957,4967,4977,4982],{"__ignoreMap":77},[81,4891,4892,4894,4897,4899,4902],{"class":83,"line":84},[81,4893,736],{"class":127},[81,4895,4896],{"class":137}," { defineConfig } ",[81,4898,742],{"class":127},[81,4900,4901],{"class":91}," \"astro\u002Fconfig\"",[81,4903,144],{"class":137},[81,4905,4906],{"class":83,"line":147},[81,4907,151],{"emptyLinePlaceholder":150},[81,4909,4910],{"class":83,"line":154},[81,4911,4912],{"class":768},"\u002F\u002F https:\u002F\u002Fastro.build\u002Fconfig\n",[81,4914,4915,4917,4920,4922,4925],{"class":83,"line":169},[81,4916,736],{"class":127},[81,4918,4919],{"class":137}," vercel ",[81,4921,742],{"class":127},[81,4923,4924],{"class":91}," \"@astrojs\u002Fvercel\u002Fserverless\"",[81,4926,144],{"class":137},[81,4928,4929],{"class":83,"line":187},[81,4930,151],{"emptyLinePlaceholder":150},[81,4932,4933],{"class":83,"line":195},[81,4934,4912],{"class":768},[81,4936,4937,4939,4942,4945],{"class":83,"line":201},[81,4938,3708],{"class":127},[81,4940,4941],{"class":127}," default",[81,4943,4944],{"class":87}," defineConfig",[81,4946,1221],{"class":137},[81,4948,4949,4952,4955],{"class":83,"line":206},[81,4950,4951],{"class":137},"  output: ",[81,4953,4954],{"class":91},"\"server\"",[81,4956,44],{"class":137},[81,4958,4959,4962,4965],{"class":83,"line":223},[81,4960,4961],{"class":137},"  adapter: ",[81,4963,4964],{"class":87},"vercel",[81,4966,1221],{"class":137},[81,4968,4969,4972,4975],{"class":83,"line":229},[81,4970,4971],{"class":137},"    includeFiles: [",[81,4973,4974],{"class":91},"\".\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf\"",[81,4976,1235],{"class":137},[81,4978,4979],{"class":83,"line":235},[81,4980,4981],{"class":137},"  }),\n",[81,4983,4984],{"class":83,"line":243},[81,4985,1300],{"class":137},[11,4987,4988,4989,4994],{},"I'm using vercel ssr adapter here to deploy this project as a Vercel serverless\nfunction. You can check out more information about it\n",[15,4990,4993],{"href":4991,"rel":4992},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fguides\u002Fserver-side-rendering\u002F#enabling-ssr-in-your-project",[19],"here",".\nMake sure you also install the adapter package",[72,4996,4998],{"className":74,"code":4997,"language":76,"meta":77,"style":77},"npm install @astrojs\u002Fvercel\n",[41,4999,5000],{"__ignoreMap":77},[81,5001,5002,5004,5006],{"class":83,"line":84},[81,5003,88],{"class":87},[81,5005,92],{"class":91},[81,5007,5008],{"class":91}," @astrojs\u002Fvercel\n",[11,5010,5011,5012,5017],{},"I'll use ",[15,5013,5016],{"href":5014,"rel":5015},"https:\u002F\u002Fvercel.com\u002Fdocs\u002Fcli",[19],"Vercel CLI"," to deploy this project. It's\na single command to deploy the project and get a preview link. Run this in the\nroot of the current project.",[72,5019,5021],{"className":74,"code":5020,"language":76,"meta":77,"style":77},"vercel\n",[41,5022,5023],{"__ignoreMap":77},[81,5024,5025],{"class":83,"line":84},[81,5026,5020],{"class":87},[11,5028,5029],{},[400,5030],{"alt":5031,"dataZoomable":77,"height":5032,"loading":404,"src":5033,"width":5034},"Deploy this using Vercel CLI",184,"\u002Fcontent\u002Fvercel_cli.png",1512,[11,5036,5037,5038,5040,5041,5045],{},"On visiting the preview link with ",[41,5039,3650],{}," path\n(",[15,5042,5043],{"href":5043,"rel":5044},"https:\u002F\u002Fastro-vercel-og-rumaan.vercel.app\u002Findex.og",[19],") I get the same OG image as\nbefore. Nice!",[29,5047,5049],{"id":5048},"update-meta-tags","Update Meta Tags",[11,5051,5052,5053,5056,5057,821],{},"Now let's set this image link as our ",[41,5054,5055],{},"og:image"," in the site metadata. In my\n",[41,5058,5059],{},"index.astro",[72,5061,5065],{"className":5062,"code":5063,"language":5064,"meta":77,"style":77},"language-astro shiki shiki-themes github-dark","\u003Chtml lang=\"en\">\n  \u003Chead>\n    \u003Cmeta charset=\"UTF-8\" \u002F>\n    \u003Cmeta http-equiv=\"x-ua-compatible\" content=\"ie=edge\" \u002F>\n    \u003Cmeta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    \u002F>\n    \u003Cmeta name=\"theme-color\" content=\"#171C1A\" \u002F>\n    \u003Cmeta name=\"color-scheme\" content=\"dark\" \u002F>\n\n    \u003Cmeta\n      property=\"og:image\"\n      content=\"https:\u002F\u002Fastro-vercel-og.vercel.app\u002Findex.og\"\n    \u002F>\n    \u003Cmeta property=\"og:image:width\" content=\"1200\" \u002F>\n    \u003Cmeta property=\"og:image:height\" content=\"630\" \u002F>\n\n    \u003Ctitle>Vercel Astro\u003C\u002Ftitle>\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Ch1>\n      Vercel Astro OG: \u003Ca href=\"\u002Findex.og\">Check here\u003C\u002Fa>\n    \u003C\u002Fh1>\n  \u003C\u002Fbody>\n\u003C\u002Fhtml>\n","astro",[41,5066,5067,5084,5092,5111,5135,5142,5152,5162,5167,5190,5212,5216,5222,5232,5241,5245,5268,5290,5294,5308,5316,5325,5334,5356,5365,5373],{"__ignoreMap":77},[81,5068,5069,5071,5073,5076,5079,5082],{"class":83,"line":84},[81,5070,425],{"class":137},[81,5072,418],{"class":428},[81,5074,5075],{"class":87}," lang",[81,5077,5078],{"class":137},"=",[81,5080,5081],{"class":91},"\"en\"",[81,5083,432],{"class":137},[81,5085,5086,5088,5090],{"class":83,"line":147},[81,5087,437],{"class":137},[81,5089,429],{"class":428},[81,5091,432],{"class":137},[81,5093,5094,5097,5100,5103,5105,5108],{"class":83,"line":154},[81,5095,5096],{"class":137},"    \u003C",[81,5098,5099],{"class":428},"meta",[81,5101,5102],{"class":87}," charset",[81,5104,5078],{"class":137},[81,5106,5107],{"class":91},"\"UTF-8\"",[81,5109,5110],{"class":137}," \u002F>\n",[81,5112,5113,5115,5117,5120,5122,5125,5128,5130,5133],{"class":83,"line":169},[81,5114,5096],{"class":137},[81,5116,5099],{"class":428},[81,5118,5119],{"class":87}," http-equiv",[81,5121,5078],{"class":137},[81,5123,5124],{"class":91},"\"x-ua-compatible\"",[81,5126,5127],{"class":87}," content",[81,5129,5078],{"class":137},[81,5131,5132],{"class":91},"\"ie=edge\"",[81,5134,5110],{"class":137},[81,5136,5137,5139],{"class":83,"line":187},[81,5138,5096],{"class":137},[81,5140,5141],{"class":428},"meta\n",[81,5143,5144,5147,5149],{"class":83,"line":195},[81,5145,5146],{"class":87},"      name",[81,5148,5078],{"class":137},[81,5150,5151],{"class":91},"\"viewport\"\n",[81,5153,5154,5157,5159],{"class":83,"line":201},[81,5155,5156],{"class":87},"      content",[81,5158,5078],{"class":137},[81,5160,5161],{"class":91},"\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n",[81,5163,5164],{"class":83,"line":206},[81,5165,5166],{"class":137},"    \u002F>\n",[81,5168,5169,5171,5173,5176,5178,5181,5183,5185,5188],{"class":83,"line":223},[81,5170,5096],{"class":137},[81,5172,5099],{"class":428},[81,5174,5175],{"class":87}," name",[81,5177,5078],{"class":137},[81,5179,5180],{"class":91},"\"theme-color\"",[81,5182,5127],{"class":87},[81,5184,5078],{"class":137},[81,5186,5187],{"class":91},"\"#171C1A\"",[81,5189,5110],{"class":137},[81,5191,5192,5194,5196,5198,5200,5203,5205,5207,5210],{"class":83,"line":229},[81,5193,5096],{"class":137},[81,5195,5099],{"class":428},[81,5197,5175],{"class":87},[81,5199,5078],{"class":137},[81,5201,5202],{"class":91},"\"color-scheme\"",[81,5204,5127],{"class":87},[81,5206,5078],{"class":137},[81,5208,5209],{"class":91},"\"dark\"",[81,5211,5110],{"class":137},[81,5213,5214],{"class":83,"line":235},[81,5215,151],{"emptyLinePlaceholder":150},[81,5217,5218,5220],{"class":83,"line":243},[81,5219,5096],{"class":137},[81,5221,5141],{"class":428},[81,5223,5224,5227,5229],{"class":83,"line":249},[81,5225,5226],{"class":87},"      property",[81,5228,5078],{"class":137},[81,5230,5231],{"class":91},"\"og:image\"\n",[81,5233,5234,5236,5238],{"class":83,"line":266},[81,5235,5156],{"class":87},[81,5237,5078],{"class":137},[81,5239,5240],{"class":91},"\"https:\u002F\u002Fastro-vercel-og.vercel.app\u002Findex.og\"\n",[81,5242,5243],{"class":83,"line":590},[81,5244,5166],{"class":137},[81,5246,5247,5249,5251,5254,5256,5259,5261,5263,5266],{"class":83,"line":596},[81,5248,5096],{"class":137},[81,5250,5099],{"class":428},[81,5252,5253],{"class":87}," property",[81,5255,5078],{"class":137},[81,5257,5258],{"class":91},"\"og:image:width\"",[81,5260,5127],{"class":87},[81,5262,5078],{"class":137},[81,5264,5265],{"class":91},"\"1200\"",[81,5267,5110],{"class":137},[81,5269,5270,5272,5274,5276,5278,5281,5283,5285,5288],{"class":83,"line":606},[81,5271,5096],{"class":137},[81,5273,5099],{"class":428},[81,5275,5253],{"class":87},[81,5277,5078],{"class":137},[81,5279,5280],{"class":91},"\"og:image:height\"",[81,5282,5127],{"class":87},[81,5284,5078],{"class":137},[81,5286,5287],{"class":91},"\"630\"",[81,5289,5110],{"class":137},[81,5291,5292],{"class":83,"line":1519},[81,5293,151],{"emptyLinePlaceholder":150},[81,5295,5296,5298,5301,5304,5306],{"class":83,"line":1530},[81,5297,5096],{"class":137},[81,5299,5300],{"class":428},"title",[81,5302,5303],{"class":137},">Vercel Astro\u003C\u002F",[81,5305,5300],{"class":428},[81,5307,432],{"class":137},[81,5309,5310,5312,5314],{"class":83,"line":1536},[81,5311,599],{"class":137},[81,5313,429],{"class":428},[81,5315,432],{"class":137},[81,5317,5318,5320,5323],{"class":83,"line":1541},[81,5319,437],{"class":137},[81,5321,5322],{"class":428},"body",[81,5324,432],{"class":137},[81,5326,5327,5329,5332],{"class":83,"line":1546},[81,5328,5096],{"class":137},[81,5330,5331],{"class":428},"h1",[81,5333,432],{"class":137},[81,5335,5336,5339,5341,5344,5346,5349,5352,5354],{"class":83,"line":1552},[81,5337,5338],{"class":137},"      Vercel Astro OG: \u003C",[81,5340,15],{"class":428},[81,5342,5343],{"class":87}," href",[81,5345,5078],{"class":137},[81,5347,5348],{"class":91},"\"\u002Findex.og\"",[81,5350,5351],{"class":137},">Check here\u003C\u002F",[81,5353,15],{"class":428},[81,5355,432],{"class":137},[81,5357,5358,5361,5363],{"class":83,"line":1561},[81,5359,5360],{"class":137},"    \u003C\u002F",[81,5362,5331],{"class":428},[81,5364,432],{"class":137},[81,5366,5367,5369,5371],{"class":83,"line":1566},[81,5368,599],{"class":137},[81,5370,5322],{"class":428},[81,5372,432],{"class":137},[81,5374,5375,5377,5379],{"class":83,"line":1572},[81,5376,609],{"class":137},[81,5378,418],{"class":428},[81,5380,432],{"class":137},[11,5382,5383],{},"This will help crawlers show the generated OG image while sharing links on their\nplatform. We can debug this by using services like:",[1737,5385,5386,5393],{},[1740,5387,5388],{},[15,5389,5392],{"href":5390,"rel":5391},"https:\u002F\u002Fdevelopers.facebook.com\u002Ftools\u002Fdebug\u002F",[19],"Facebook Share Debugger",[1740,5394,5395],{},[15,5396,5399],{"href":5397,"rel":5398},"https:\u002F\u002Fwww.opengraph.xyz\u002F",[19],"OpenGraph.xyz",[29,5401,5403],{"id":5402},"generate-dynamic-og-images","Generate Dynamic OG Images",[11,5405,5406,5407,5414],{},"Astro Server Endpoints give access to the\n",[15,5408,5411],{"href":5409,"rel":5410},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fcore-concepts\u002Fendpoints\u002F#request",[19],[41,5412,5413],{},"Request"," Object\nwhich contains query and path params using which we can generate dynamic open\ngraph images. Let's say we had a bunch of blog post articles (.md) and we want\nto generate OG images containing the blog post title.",[11,5416,5417,5418,2446,5421,5424,5425,5428],{},"I've created two markdown files ",[41,5419,5420],{},"post1.md",[41,5422,5423],{},"post2.md"," under ",[41,5426,5427],{},"\u002Fpages\u002Fblog\u002F","\ndirectory with a simple frontmatter containing a title and a reference to the\nlayout file.",[72,5430,5433],{"className":5431,"code":5432,"language":1763,"meta":77,"style":77},"language-md shiki shiki-themes github-dark","---\ntitle: \"Hello World\"\nlayout: \"..\u002F..\u002Flayouts\u002FBlogLayout.astro\"\n---\n\nHello World content\n",[41,5434,5435,5441,5446,5451,5455,5459],{"__ignoreMap":77},[81,5436,5437],{"class":83,"line":84},[81,5438,5440],{"class":5439},"sIZOC","---\n",[81,5442,5443],{"class":83,"line":147},[81,5444,5445],{"class":137},"title: \"Hello World\"\n",[81,5447,5448],{"class":83,"line":154},[81,5449,5450],{"class":137},"layout: \"..\u002F..\u002Flayouts\u002FBlogLayout.astro\"\n",[81,5452,5453],{"class":83,"line":169},[81,5454,5440],{"class":5439},[81,5456,5457],{"class":83,"line":187},[81,5458,151],{"emptyLinePlaceholder":150},[81,5460,5461],{"class":83,"line":195},[81,5462,5463],{"class":137},"Hello World content\n",[11,5465,5466],{},[400,5467],{"alt":5468,"dataZoomable":77,"height":5469,"loading":404,"src":5470,"width":5471},"Simple Blog Directory Setup with Markdown Files",638,"\u002Fcontent\u002Fblog_directory.png",468,[11,5473,272,5474,821],{},[41,5475,5476],{},"layouts\u002FBlogLayout.astro",[72,5478,5480],{"className":5062,"code":5479,"language":5064,"meta":77,"style":77},"---\nconst { frontmatter } = Astro.props;\n---\n\n\u003Cmain>\n  \u003Ch1>{frontmatter.title}\u003C\u002Fh1>\n  \u003Cslot \u002F>\n\u003C\u002Fmain>\n",[41,5481,5482,5486,5504,5508,5512,5521,5534,5543],{"__ignoreMap":77},[81,5483,5484],{"class":83,"line":84},[81,5485,5440],{"class":768},[81,5487,5488,5490,5493,5496,5499,5501],{"class":83,"line":147},[81,5489,128],{"class":127},[81,5491,5492],{"class":137}," { ",[81,5494,5495],{"class":95},"frontmatter",[81,5497,5498],{"class":137}," } ",[81,5500,5078],{"class":127},[81,5502,5503],{"class":137}," Astro.props;\n",[81,5505,5506],{"class":83,"line":154},[81,5507,5440],{"class":768},[81,5509,5510],{"class":83,"line":169},[81,5511,151],{"emptyLinePlaceholder":150},[81,5513,5514,5516,5519],{"class":83,"line":187},[81,5515,425],{"class":137},[81,5517,5518],{"class":428},"main",[81,5520,432],{"class":137},[81,5522,5523,5525,5527,5530,5532],{"class":83,"line":195},[81,5524,437],{"class":137},[81,5526,5331],{"class":428},[81,5528,5529],{"class":137},">{frontmatter.title}\u003C\u002F",[81,5531,5331],{"class":428},[81,5533,432],{"class":137},[81,5535,5536,5538,5541],{"class":83,"line":201},[81,5537,437],{"class":137},[81,5539,5540],{"class":428},"slot",[81,5542,5110],{"class":137},[81,5544,5545,5547,5549],{"class":83,"line":206},[81,5546,609],{"class":137},[81,5548,5518],{"class":428},[81,5550,432],{"class":137},[11,5552,5553,5554,5559,5560,5563,5564,5566,5567,5569],{},"Now let's create a server endpoint file (I'll call it [slug].og.ts) which is\nalso a\n",[15,5555,5558],{"href":5556,"rel":5557},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fcore-concepts\u002Frouting\u002F#dynamic-routes",[19],"Dynamic Route","\nwith path param called ",[41,5561,5562],{},"slug"," which can be later referenced in our code. We'll\ncreate a similar GET route and access the ",[41,5565,5562],{}," path param from the ",[41,5568,5413],{},"\nobject.",[11,5571,5572,821],{},[41,5573,5574],{},"[slug].og.ts",[72,5576,5578],{"className":3678,"code":5577,"language":3680,"meta":77,"style":77},"export async function GET({ params }: APIContext) {\n  const { slug } = params;\n};\n",[41,5579,5580,5602,5617],{"__ignoreMap":77},[81,5581,5582,5584,5586,5588,5590,5592,5594,5596,5598,5600],{"class":83,"line":84},[81,5583,3708],{"class":127},[81,5585,3711],{"class":127},[81,5587,3714],{"class":127},[81,5589,3717],{"class":87},[81,5591,3720],{"class":137},[81,5593,3723],{"class":547},[81,5595,3726],{"class":137},[81,5597,115],{"class":127},[81,5599,3731],{"class":87},[81,5601,3799],{"class":137},[81,5603,5604,5606,5608,5610,5612,5614],{"class":83,"line":147},[81,5605,3804],{"class":127},[81,5607,5492],{"class":137},[81,5609,5562],{"class":95},[81,5611,5498],{"class":137},[81,5613,5078],{"class":127},[81,5615,5616],{"class":137}," params;\n",[81,5618,5619],{"class":83,"line":154},[81,5620,269],{"class":137},[11,5622,5623,5624,5626,5627,5629,5630,5637],{},"Using the ",[41,5625,5562],{}," we'll find the corresponding Blog post markdown file and access\nit's frontmatter that contains the ",[41,5628,5300],{}," and use that in our html template for\ngenerating og image. I'll use\n",[15,5631,5634],{"href":5632,"rel":5633},"https:\u002F\u002Fvitejs.dev\u002Fguide\u002Fmigration.html#import-meta-glob",[19],[41,5635,5636],{},"import.meta.glob","\nfrom Vite to get all the markdown files in a directory, find the markdown file\nmatching slug, load that markdown file and get the post title.",[72,5639,5641],{"className":3678,"code":5640,"language":3680,"meta":77,"style":77},"\u002F\u002F other imports...\nimport { readFileSync } from \"fs\";\nimport { basename } from \"path\";\nimport type { APIContext, MarkdownInstance } from \"astro\";\n\nexport async function GET({ params }: APIContext) {\n  const { slug } = params;\n  \u002F\u002F Find the slug in content dir\n  const posts: Record\u003Cstring, () => Promise\u003Cany>> = import.meta.glob(\n    `.\u002F**\u002F*.md`\n  );\n\n  const postPaths = Object.entries(posts).map(([path, promise]) => ({\n    slug: basename(path).replace(\".md\", \"\"),\n    loadPost: promise,\n  }));\n\n  const post = postPaths.find((p) => p.slug === String(slug));\n  let postTitle = `My Blog`; \u002F\u002F Default title if post not found\n  if (post) {\n    const postData = (await post.loadPost()) as MarkdownInstance\u003C\n      Record\u003Cstring, any>\n    >;\n    postTitle = postData.frontmatter.title;\n  }\n  \u002F\u002F ...\n};\n",[41,5642,5643,5648,5660,5674,5689,5693,5715,5729,5734,5782,5787,5792,5796,5836,5863,5868,5873,5877,5912,5931,5939,5971,5986,5991,6001,6005,6009],{"__ignoreMap":77},[81,5644,5645],{"class":83,"line":84},[81,5646,5647],{"class":768},"\u002F\u002F other imports...\n",[81,5649,5650,5652,5654,5656,5658],{"class":83,"line":147},[81,5651,736],{"class":127},[81,5653,3944],{"class":137},[81,5655,742],{"class":127},[81,5657,3949],{"class":91},[81,5659,144],{"class":137},[81,5661,5662,5664,5667,5669,5672],{"class":83,"line":154},[81,5663,736],{"class":127},[81,5665,5666],{"class":137}," { basename } ",[81,5668,742],{"class":127},[81,5670,5671],{"class":91}," \"path\"",[81,5673,144],{"class":137},[81,5675,5676,5678,5680,5683,5685,5687],{"class":83,"line":169},[81,5677,736],{"class":127},[81,5679,3689],{"class":127},[81,5681,5682],{"class":137}," { APIContext, MarkdownInstance } ",[81,5684,742],{"class":127},[81,5686,3697],{"class":91},[81,5688,144],{"class":137},[81,5690,5691],{"class":83,"line":187},[81,5692,151],{"emptyLinePlaceholder":150},[81,5694,5695,5697,5699,5701,5703,5705,5707,5709,5711,5713],{"class":83,"line":195},[81,5696,3708],{"class":127},[81,5698,3711],{"class":127},[81,5700,3714],{"class":127},[81,5702,3717],{"class":87},[81,5704,3720],{"class":137},[81,5706,3723],{"class":547},[81,5708,3726],{"class":137},[81,5710,115],{"class":127},[81,5712,3731],{"class":87},[81,5714,3799],{"class":137},[81,5716,5717,5719,5721,5723,5725,5727],{"class":83,"line":201},[81,5718,3804],{"class":127},[81,5720,5492],{"class":137},[81,5722,5562],{"class":95},[81,5724,5498],{"class":137},[81,5726,5078],{"class":127},[81,5728,5616],{"class":137},[81,5730,5731],{"class":83,"line":206},[81,5732,5733],{"class":768},"  \u002F\u002F Find the slug in content dir\n",[81,5735,5736,5738,5741,5743,5746,5748,5751,5753,5755,5758,5760,5763,5766,5768,5771,5773,5775,5777,5780],{"class":83,"line":223},[81,5737,3804],{"class":127},[81,5739,5740],{"class":95}," posts",[81,5742,115],{"class":127},[81,5744,5745],{"class":87}," Record",[81,5747,425],{"class":137},[81,5749,5750],{"class":95},"string",[81,5752,458],{"class":137},[81,5754,461],{"class":127},[81,5756,5757],{"class":87}," Promise",[81,5759,425],{"class":137},[81,5761,5762],{"class":95},"any",[81,5764,5765],{"class":137},">> ",[81,5767,5078],{"class":127},[81,5769,5770],{"class":127}," import",[81,5772,212],{"class":137},[81,5774,5099],{"class":95},[81,5776,212],{"class":137},[81,5778,5779],{"class":87},"glob",[81,5781,1506],{"class":137},[81,5783,5784],{"class":83,"line":229},[81,5785,5786],{"class":91},"    `.\u002F**\u002F*.md`\n",[81,5788,5789],{"class":83,"line":235},[81,5790,5791],{"class":137},"  );\n",[81,5793,5794],{"class":83,"line":243},[81,5795,151],{"emptyLinePlaceholder":150},[81,5797,5798,5800,5803,5805,5808,5811,5814,5817,5820,5823,5825,5828,5831,5833],{"class":83,"line":249},[81,5799,3804],{"class":127},[81,5801,5802],{"class":95}," postPaths",[81,5804,134],{"class":127},[81,5806,5807],{"class":137}," Object.",[81,5809,5810],{"class":87},"entries",[81,5812,5813],{"class":137},"(posts).",[81,5815,5816],{"class":87},"map",[81,5818,5819],{"class":137},"(([",[81,5821,5822],{"class":547},"path",[81,5824,933],{"class":137},[81,5826,5827],{"class":547},"promise",[81,5829,5830],{"class":137},"]) ",[81,5832,461],{"class":127},[81,5834,5835],{"class":137}," ({\n",[81,5837,5838,5841,5844,5847,5850,5852,5855,5857,5860],{"class":83,"line":266},[81,5839,5840],{"class":137},"    slug: ",[81,5842,5843],{"class":87},"basename",[81,5845,5846],{"class":137},"(path).",[81,5848,5849],{"class":87},"replace",[81,5851,178],{"class":137},[81,5853,5854],{"class":91},"\".md\"",[81,5856,933],{"class":137},[81,5858,5859],{"class":91},"\"\"",[81,5861,5862],{"class":137},"),\n",[81,5864,5865],{"class":83,"line":590},[81,5866,5867],{"class":137},"    loadPost: promise,\n",[81,5869,5870],{"class":83,"line":596},[81,5871,5872],{"class":137},"  }));\n",[81,5874,5875],{"class":83,"line":606},[81,5876,151],{"emptyLinePlaceholder":150},[81,5878,5879,5881,5884,5886,5889,5892,5894,5896,5898,5900,5903,5906,5909],{"class":83,"line":1519},[81,5880,3804],{"class":127},[81,5882,5883],{"class":95}," post",[81,5885,134],{"class":127},[81,5887,5888],{"class":137}," postPaths.",[81,5890,5891],{"class":87},"find",[81,5893,544],{"class":137},[81,5895,11],{"class":547},[81,5897,551],{"class":137},[81,5899,461],{"class":127},[81,5901,5902],{"class":137}," p.slug ",[81,5904,5905],{"class":127},"===",[81,5907,5908],{"class":87}," String",[81,5910,5911],{"class":137},"(slug));\n",[81,5913,5914,5917,5920,5922,5925,5928],{"class":83,"line":1530},[81,5915,5916],{"class":127},"  let",[81,5918,5919],{"class":137}," postTitle ",[81,5921,5078],{"class":127},[81,5923,5924],{"class":91}," `My Blog`",[81,5926,5927],{"class":137},"; ",[81,5929,5930],{"class":768},"\u002F\u002F Default title if post not found\n",[81,5932,5933,5936],{"class":83,"line":1536},[81,5934,5935],{"class":127},"  if",[81,5937,5938],{"class":137}," (post) {\n",[81,5940,5941,5944,5947,5949,5951,5953,5956,5959,5962,5965,5968],{"class":83,"line":1541},[81,5942,5943],{"class":127},"    const",[81,5945,5946],{"class":95}," postData",[81,5948,134],{"class":127},[81,5950,160],{"class":137},[81,5952,1212],{"class":127},[81,5954,5955],{"class":137}," post.",[81,5957,5958],{"class":87},"loadPost",[81,5960,5961],{"class":137},"()) ",[81,5963,5964],{"class":127},"as",[81,5966,5967],{"class":87}," MarkdownInstance",[81,5969,5970],{"class":137},"\u003C\n",[81,5972,5973,5976,5978,5980,5982,5984],{"class":83,"line":1546},[81,5974,5975],{"class":87},"      Record",[81,5977,425],{"class":137},[81,5979,5750],{"class":95},[81,5981,933],{"class":137},[81,5983,5762],{"class":95},[81,5985,432],{"class":137},[81,5987,5988],{"class":83,"line":1552},[81,5989,5990],{"class":137},"    >;\n",[81,5992,5993,5996,5998],{"class":83,"line":1561},[81,5994,5995],{"class":137},"    postTitle ",[81,5997,5078],{"class":127},[81,5999,6000],{"class":137}," postData.frontmatter.title;\n",[81,6002,6003],{"class":83,"line":1566},[81,6004,363],{"class":137},[81,6006,6007],{"class":83,"line":1572},[81,6008,4668],{"class":768},[81,6010,6011],{"class":83,"line":1578},[81,6012,269],{"class":137},[11,6014,6015],{},"Now that we have the post title, let's inject it into our html markup",[72,6017,6019],{"className":3678,"code":6018,"language":3680,"meta":77,"style":77},"import satori from \"satori\";\nimport { html } from \"satori-html\";\nimport { readFileSync } from \"fs\";\nimport type { APIContext, MarkdownInstance } from \"astro\";\nimport sharp from \"sharp\";\nimport { basename } from \"path\";\n\nexport async function GET({ params }: APIContext) {\n  const { slug } = params;\n  \u002F\u002F Find the slug in content dir\n  const posts: Record\u003Cstring, () => Promise\u003Cany>> = import.meta.glob(\n    `.\u002F**\u002F*.md`\n  );\n\n  const postPaths = Object.entries(posts).map(([path, promise]) => ({\n    slug: basename(path).replace(\".md\", \"\"),\n    loadPost: promise,\n  }));\n\n  const post = postPaths.find((p) => p.slug === String(slug));\n  let postTitle = `My Blog`; \u002F\u002F Default title if post not found\n  if (post) {\n    const postData = (await post.loadPost()) as MarkdownInstance\u003C\n      Record\u003Cstring, any>\n    >;\n    postTitle = postData.frontmatter.title;\n  }\n\n  const fontFilePath = `${process.cwd()}\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf`;\n\n  const fontFile = readFileSync(fontFilePath);\n\n  const markup = html(`\n  \u003Cdiv\n    style=\"height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgb(45,26,84); font-size: 32px; font-weight: 600;\"\n  >\n    \u003Cdiv\n      style=\"font-size: 70px; margin-top: 38px; display: flex; flex-direction: column; color: white;\"\n    >\n      ${postTitle}\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>`);\n  const svg = await satori(markup, {\n    width: 1200,\n    height: 630,\n    fonts: [\n      {\n        name: \"Optimistic Display\",\n        data: fontFile,\n        style: \"normal\",\n      },\n    ],\n  });\n\n  const png = sharp(Buffer.from(svg)).png();\n  const response = await png.toBuffer();\n\n  return new Response(response, {\n    status: 200,\n    headers: {\n      \"Content-Type\": \"image\u002Fpng\",\n    },\n  });\n};\n",[41,6020,6021,6033,6045,6057,6071,6083,6095,6099,6121,6135,6139,6179,6183,6187,6191,6221,6241,6245,6249,6253,6281,6295,6301,6325,6339,6343,6351,6355,6359,6381,6385,6397,6401,6415,6419,6423,6427,6431,6435,6439,6449,6453,6459,6473,6481,6489,6493,6497,6505,6509,6517,6521,6525,6529,6533,6553,6569,6573,6583,6591,6595,6605,6609,6613],{"__ignoreMap":77},[81,6022,6023,6025,6027,6029,6031],{"class":83,"line":84},[81,6024,736],{"class":127},[81,6026,4260],{"class":137},[81,6028,742],{"class":127},[81,6030,4265],{"class":91},[81,6032,144],{"class":137},[81,6034,6035,6037,6039,6041,6043],{"class":83,"line":147},[81,6036,736],{"class":127},[81,6038,3765],{"class":137},[81,6040,742],{"class":127},[81,6042,3770],{"class":91},[81,6044,144],{"class":137},[81,6046,6047,6049,6051,6053,6055],{"class":83,"line":154},[81,6048,736],{"class":127},[81,6050,3944],{"class":137},[81,6052,742],{"class":127},[81,6054,3949],{"class":91},[81,6056,144],{"class":137},[81,6058,6059,6061,6063,6065,6067,6069],{"class":83,"line":169},[81,6060,736],{"class":127},[81,6062,3689],{"class":127},[81,6064,5682],{"class":137},[81,6066,742],{"class":127},[81,6068,3697],{"class":91},[81,6070,144],{"class":137},[81,6072,6073,6075,6077,6079,6081],{"class":83,"line":187},[81,6074,736],{"class":127},[81,6076,4630],{"class":137},[81,6078,742],{"class":127},[81,6080,4635],{"class":91},[81,6082,144],{"class":137},[81,6084,6085,6087,6089,6091,6093],{"class":83,"line":195},[81,6086,736],{"class":127},[81,6088,5666],{"class":137},[81,6090,742],{"class":127},[81,6092,5671],{"class":91},[81,6094,144],{"class":137},[81,6096,6097],{"class":83,"line":201},[81,6098,151],{"emptyLinePlaceholder":150},[81,6100,6101,6103,6105,6107,6109,6111,6113,6115,6117,6119],{"class":83,"line":206},[81,6102,3708],{"class":127},[81,6104,3711],{"class":127},[81,6106,3714],{"class":127},[81,6108,3717],{"class":87},[81,6110,3720],{"class":137},[81,6112,3723],{"class":547},[81,6114,3726],{"class":137},[81,6116,115],{"class":127},[81,6118,3731],{"class":87},[81,6120,3799],{"class":137},[81,6122,6123,6125,6127,6129,6131,6133],{"class":83,"line":223},[81,6124,3804],{"class":127},[81,6126,5492],{"class":137},[81,6128,5562],{"class":95},[81,6130,5498],{"class":137},[81,6132,5078],{"class":127},[81,6134,5616],{"class":137},[81,6136,6137],{"class":83,"line":229},[81,6138,5733],{"class":768},[81,6140,6141,6143,6145,6147,6149,6151,6153,6155,6157,6159,6161,6163,6165,6167,6169,6171,6173,6175,6177],{"class":83,"line":235},[81,6142,3804],{"class":127},[81,6144,5740],{"class":95},[81,6146,115],{"class":127},[81,6148,5745],{"class":87},[81,6150,425],{"class":137},[81,6152,5750],{"class":95},[81,6154,458],{"class":137},[81,6156,461],{"class":127},[81,6158,5757],{"class":87},[81,6160,425],{"class":137},[81,6162,5762],{"class":95},[81,6164,5765],{"class":137},[81,6166,5078],{"class":127},[81,6168,5770],{"class":127},[81,6170,212],{"class":137},[81,6172,5099],{"class":95},[81,6174,212],{"class":137},[81,6176,5779],{"class":87},[81,6178,1506],{"class":137},[81,6180,6181],{"class":83,"line":243},[81,6182,5786],{"class":91},[81,6184,6185],{"class":83,"line":249},[81,6186,5791],{"class":137},[81,6188,6189],{"class":83,"line":266},[81,6190,151],{"emptyLinePlaceholder":150},[81,6192,6193,6195,6197,6199,6201,6203,6205,6207,6209,6211,6213,6215,6217,6219],{"class":83,"line":590},[81,6194,3804],{"class":127},[81,6196,5802],{"class":95},[81,6198,134],{"class":127},[81,6200,5807],{"class":137},[81,6202,5810],{"class":87},[81,6204,5813],{"class":137},[81,6206,5816],{"class":87},[81,6208,5819],{"class":137},[81,6210,5822],{"class":547},[81,6212,933],{"class":137},[81,6214,5827],{"class":547},[81,6216,5830],{"class":137},[81,6218,461],{"class":127},[81,6220,5835],{"class":137},[81,6222,6223,6225,6227,6229,6231,6233,6235,6237,6239],{"class":83,"line":596},[81,6224,5840],{"class":137},[81,6226,5843],{"class":87},[81,6228,5846],{"class":137},[81,6230,5849],{"class":87},[81,6232,178],{"class":137},[81,6234,5854],{"class":91},[81,6236,933],{"class":137},[81,6238,5859],{"class":91},[81,6240,5862],{"class":137},[81,6242,6243],{"class":83,"line":606},[81,6244,5867],{"class":137},[81,6246,6247],{"class":83,"line":1519},[81,6248,5872],{"class":137},[81,6250,6251],{"class":83,"line":1530},[81,6252,151],{"emptyLinePlaceholder":150},[81,6254,6255,6257,6259,6261,6263,6265,6267,6269,6271,6273,6275,6277,6279],{"class":83,"line":1536},[81,6256,3804],{"class":127},[81,6258,5883],{"class":95},[81,6260,134],{"class":127},[81,6262,5888],{"class":137},[81,6264,5891],{"class":87},[81,6266,544],{"class":137},[81,6268,11],{"class":547},[81,6270,551],{"class":137},[81,6272,461],{"class":127},[81,6274,5902],{"class":137},[81,6276,5905],{"class":127},[81,6278,5908],{"class":87},[81,6280,5911],{"class":137},[81,6282,6283,6285,6287,6289,6291,6293],{"class":83,"line":1541},[81,6284,5916],{"class":127},[81,6286,5919],{"class":137},[81,6288,5078],{"class":127},[81,6290,5924],{"class":91},[81,6292,5927],{"class":137},[81,6294,5930],{"class":768},[81,6296,6297,6299],{"class":83,"line":1546},[81,6298,5935],{"class":127},[81,6300,5938],{"class":137},[81,6302,6303,6305,6307,6309,6311,6313,6315,6317,6319,6321,6323],{"class":83,"line":1552},[81,6304,5943],{"class":127},[81,6306,5946],{"class":95},[81,6308,134],{"class":127},[81,6310,160],{"class":137},[81,6312,1212],{"class":127},[81,6314,5955],{"class":137},[81,6316,5958],{"class":87},[81,6318,5961],{"class":137},[81,6320,5964],{"class":127},[81,6322,5967],{"class":87},[81,6324,5970],{"class":137},[81,6326,6327,6329,6331,6333,6335,6337],{"class":83,"line":1561},[81,6328,5975],{"class":87},[81,6330,425],{"class":137},[81,6332,5750],{"class":95},[81,6334,933],{"class":137},[81,6336,5762],{"class":95},[81,6338,432],{"class":137},[81,6340,6341],{"class":83,"line":1566},[81,6342,5990],{"class":137},[81,6344,6345,6347,6349],{"class":83,"line":1572},[81,6346,5995],{"class":137},[81,6348,5078],{"class":127},[81,6350,6000],{"class":137},[81,6352,6353],{"class":83,"line":1578},[81,6354,363],{"class":137},[81,6356,6357],{"class":83,"line":1584},[81,6358,151],{"emptyLinePlaceholder":150},[81,6360,6361,6363,6365,6367,6369,6371,6373,6375,6377,6379],{"class":83,"line":1601},[81,6362,3804],{"class":127},[81,6364,4060],{"class":95},[81,6366,134],{"class":127},[81,6368,4065],{"class":91},[81,6370,4068],{"class":137},[81,6372,212],{"class":91},[81,6374,4073],{"class":87},[81,6376,4076],{"class":91},[81,6378,4079],{"class":91},[81,6380,144],{"class":137},[81,6382,6383],{"class":83,"line":1617},[81,6384,151],{"emptyLinePlaceholder":150},[81,6386,6387,6389,6391,6393,6395],{"class":83,"line":1648},[81,6388,3804],{"class":127},[81,6390,4092],{"class":95},[81,6392,134],{"class":127},[81,6394,4097],{"class":87},[81,6396,4100],{"class":137},[81,6398,6399],{"class":83,"line":1653},[81,6400,151],{"emptyLinePlaceholder":150},[81,6402,6403,6405,6407,6409,6411,6413],{"class":83,"line":1663},[81,6404,3804],{"class":127},[81,6406,3807],{"class":95},[81,6408,134],{"class":127},[81,6410,3812],{"class":87},[81,6412,178],{"class":137},[81,6414,3817],{"class":91},[81,6416,6417],{"class":83,"line":1673},[81,6418,3822],{"class":91},[81,6420,6421],{"class":83,"line":1678},[81,6422,3827],{"class":91},[81,6424,6425],{"class":83,"line":1683},[81,6426,3832],{"class":91},[81,6428,6429],{"class":83,"line":1688},[81,6430,3837],{"class":91},[81,6432,6433],{"class":83,"line":1694},[81,6434,3842],{"class":91},[81,6436,6437],{"class":83,"line":3041},[81,6438,3847],{"class":91},[81,6440,6441,6444,6447],{"class":83,"line":3047},[81,6442,6443],{"class":91},"      ${",[81,6445,6446],{"class":137},"postTitle",[81,6448,198],{"class":91},[81,6450,6451],{"class":83,"line":3053},[81,6452,3882],{"class":91},[81,6454,6455,6457],{"class":83,"line":3059},[81,6456,3887],{"class":91},[81,6458,184],{"class":137},[81,6460,6461,6463,6465,6467,6469,6471],{"class":83,"line":3065},[81,6462,3804],{"class":127},[81,6464,4111],{"class":95},[81,6466,134],{"class":127},[81,6468,4116],{"class":127},[81,6470,3637],{"class":87},[81,6472,4121],{"class":137},[81,6474,6475,6477,6479],{"class":83,"line":3071},[81,6476,4126],{"class":137},[81,6478,4129],{"class":95},[81,6480,44],{"class":137},[81,6482,6483,6485,6487],{"class":83,"line":3077},[81,6484,4136],{"class":137},[81,6486,4139],{"class":95},[81,6488,44],{"class":137},[81,6490,6491],{"class":83,"line":3083},[81,6492,4146],{"class":137},[81,6494,6495],{"class":83,"line":3089},[81,6496,4151],{"class":137},[81,6498,6499,6501,6503],{"class":83,"line":3095},[81,6500,4156],{"class":137},[81,6502,4159],{"class":91},[81,6504,44],{"class":137},[81,6506,6507],{"class":83,"line":3101},[81,6508,4166],{"class":137},[81,6510,6511,6513,6515],{"class":83,"line":3107},[81,6512,4171],{"class":137},[81,6514,4174],{"class":91},[81,6516,44],{"class":137},[81,6518,6519],{"class":83,"line":3113},[81,6520,4181],{"class":137},[81,6522,6523],{"class":83,"line":3119},[81,6524,4186],{"class":137},[81,6526,6527],{"class":83,"line":3125},[81,6528,4191],{"class":137},[81,6530,6531],{"class":83,"line":3131},[81,6532,151],{"emptyLinePlaceholder":150},[81,6534,6535,6537,6539,6541,6543,6545,6547,6549,6551],{"class":83,"line":3137},[81,6536,3804],{"class":127},[81,6538,4675],{"class":95},[81,6540,134],{"class":127},[81,6542,4680],{"class":87},[81,6544,4683],{"class":137},[81,6546,742],{"class":87},[81,6548,4688],{"class":137},[81,6550,4691],{"class":87},[81,6552,759],{"class":137},[81,6554,6555,6557,6559,6561,6563,6565,6567],{"class":83,"line":3143},[81,6556,3804],{"class":127},[81,6558,4700],{"class":95},[81,6560,134],{"class":127},[81,6562,4116],{"class":127},[81,6564,4707],{"class":137},[81,6566,4710],{"class":87},[81,6568,759],{"class":137},[81,6570,6571],{"class":83,"line":3154},[81,6572,151],{"emptyLinePlaceholder":150},[81,6574,6575,6577,6579,6581],{"class":83,"line":3160},[81,6576,190],{"class":127},[81,6578,1500],{"class":127},[81,6580,4504],{"class":87},[81,6582,4819],{"class":137},[81,6584,6585,6587,6589],{"class":83,"line":3165},[81,6586,4512],{"class":137},[81,6588,4515],{"class":95},[81,6590,44],{"class":137},[81,6592,6593],{"class":83,"line":3170},[81,6594,4522],{"class":137},[81,6596,6597,6599,6601,6603],{"class":83,"line":3177},[81,6598,4527],{"class":91},[81,6600,343],{"class":137},[81,6602,4840],{"class":91},[81,6604,44],{"class":137},[81,6606,6607],{"class":83,"line":3182},[81,6608,4539],{"class":137},[81,6610,6611],{"class":83,"line":3189},[81,6612,4191],{"class":137},[81,6614,6615],{"class":83,"line":3194},[81,6616,269],{"class":137},[11,6618,6619,6620,6623,6624,115],{},"If we visit ",[41,6621,6622],{},"\u002Fblog\u002F[slug].og"," path we should get an image with the corresponding\nblog post title in it. Let's try ",[41,6625,6626],{},"\u002Fblog\u002Fpost1.og",[11,6628,6629],{},[400,6630],{"alt":6631,"dataZoomable":77,"height":4866,"loading":404,"src":6632,"width":6633},"Dynamic OG image with Blog Post title","\u002Fcontent\u002Fpost_og.png",2804,[29,6635,6637],{"id":6636},"update-astro-20-content-collections","Update: Astro 2.0 Content Collections",[11,6639,6640,6645],{},[15,6641,6644],{"href":6642,"rel":6643},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Fguides\u002Fcontent-collections\u002F",[19],"Content Collections API","\nin Astro 2.0 simplifies a lot of code required to query and access the\nMarkdown\u002FMDX file contents in a Type-safe way.",[11,6647,6648,6649,6652],{},"Assuming we have a content collections for ",[41,6650,6651],{},"posts"," set up like so:",[11,6654,6655],{},[400,6656],{"alt":6657,"dataZoomable":77,"height":6658,"loading":404,"src":6659,"width":6660},"Content Collections in Astro 2.0",634,"\u002Fcontent\u002Fcollection_directory.png",444,[11,6662,6663,6664,6667],{},"With Collection ",[41,6665,6666],{},"src\u002Fcontent\u002Fconfig.ts"," file as:",[72,6669,6671],{"className":3678,"code":6670,"language":3680,"meta":77,"style":77},"import { defineCollection, z } from \"astro:content\";\n\nconst postsCollection = defineCollection({\n  schema: z.object({\n    title: z.string(),\n    description: z.string(),\n    \u002F\u002F ...\n  }),\n});\n\nexport const collections = {\n  post: postsCollection,\n};\n",[41,6672,6673,6687,6691,6705,6715,6725,6734,6739,6743,6747,6751,6765,6770],{"__ignoreMap":77},[81,6674,6675,6677,6680,6682,6685],{"class":83,"line":84},[81,6676,736],{"class":127},[81,6678,6679],{"class":137}," { defineCollection, z } ",[81,6681,742],{"class":127},[81,6683,6684],{"class":91}," \"astro:content\"",[81,6686,144],{"class":137},[81,6688,6689],{"class":83,"line":147},[81,6690,151],{"emptyLinePlaceholder":150},[81,6692,6693,6695,6698,6700,6703],{"class":83,"line":154},[81,6694,128],{"class":127},[81,6696,6697],{"class":95}," postsCollection",[81,6699,134],{"class":127},[81,6701,6702],{"class":87}," defineCollection",[81,6704,1221],{"class":137},[81,6706,6707,6710,6713],{"class":83,"line":169},[81,6708,6709],{"class":137},"  schema: z.",[81,6711,6712],{"class":87},"object",[81,6714,1221],{"class":137},[81,6716,6717,6720,6722],{"class":83,"line":187},[81,6718,6719],{"class":137},"    title: z.",[81,6721,5750],{"class":87},[81,6723,6724],{"class":137},"(),\n",[81,6726,6727,6730,6732],{"class":83,"line":195},[81,6728,6729],{"class":137},"    description: z.",[81,6731,5750],{"class":87},[81,6733,6724],{"class":137},[81,6735,6736],{"class":83,"line":201},[81,6737,6738],{"class":768},"    \u002F\u002F ...\n",[81,6740,6741],{"class":83,"line":206},[81,6742,4981],{"class":137},[81,6744,6745],{"class":83,"line":223},[81,6746,1300],{"class":137},[81,6748,6749],{"class":83,"line":229},[81,6750,151],{"emptyLinePlaceholder":150},[81,6752,6753,6755,6758,6761,6763],{"class":83,"line":235},[81,6754,3708],{"class":127},[81,6756,6757],{"class":127}," const",[81,6759,6760],{"class":95}," collections",[81,6762,134],{"class":127},[81,6764,220],{"class":137},[81,6766,6767],{"class":83,"line":243},[81,6768,6769],{"class":137},"  post: postsCollection,\n",[81,6771,6772],{"class":83,"line":249},[81,6773,269],{"class":137},[11,6775,6776,6777,6784],{},"Now using Content Collection Query function\n",[15,6778,6781],{"href":6779,"rel":6780},"https:\u002F\u002Fdocs.astro.build\u002Fen\u002Freference\u002Fapi-reference\u002F#getentrybyslug",[19],[41,6782,6783],{},"getEntryBySlug","\nwe can query the post by slug in just one line and render our OG image with the\nrequired data from the post.",[11,6786,6787,115],{},[41,6788,6789],{},"pages\u002Fpost\u002F[slug].og.ts",[72,6791,6793],{"className":3678,"code":6792,"language":3680,"meta":77,"style":77},"import type { APIContext } from \"astro\";\nimport { getEntryBySlug } from \"astro:content\";\nimport { readFileSync } from \"node:fs\";\nimport { html } from \"satori-html\";\nimport satori from \"satori\";\nimport sharp from \"sharp\";\n\nexport async function GET({ params }: APIContext) {\n  const { slug } = params;\n  \u002F\u002F\u002F One line to get the post from our collection using slug\n  const post = await getEntryBySlug(\"post\", slug!!);\n  const title = post?.data.title ?? \"My Post\";\n\n  const fontFilePath = `${process.cwd()}\u002Fpublic\u002Ffonts\u002FOptimistic_Display_Bold.ttf`;\n  const fontFile = readFileSync(fontFilePath);\n\n  const markup = html(`\u003Cdiv\n    style=\"height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: rgb(45,26,84); font-size: 32px; font-weight: 600;\"\n  >\n    \u003Cdiv\n      style=\"font-size: 70px; margin-top: 38px; display: flex; flex-direction: column; color: white;\"\n    >\n      ${title}\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>`);\n  const svg = await satori(markup, {\n    width: 1200,\n    height: 630,\n    fonts: [\n      {\n        name: \"Optimistic Display\",\n        data: fontFile,\n        style: \"normal\",\n      },\n    ],\n  });\n  const png = sharp(Buffer.from(svg)).png();\n  const response = await png.toBuffer();\n\n  return new Response(response, {\n    status: 200,\n    headers: {\n      \"Content-Type\": \"image\u002Fpng\",\n      \"Cache-Control\": \"s-maxage=1, stale-while-revalidate=59\",\n    },\n  });\n};\n",[41,6794,6795,6809,6822,6835,6847,6859,6871,6875,6897,6911,6916,6942,6962,6966,6988,7000,7004,7019,7023,7027,7031,7035,7039,7047,7051,7057,7071,7079,7087,7091,7095,7103,7107,7115,7119,7123,7127,7147,7163,7167,7177,7185,7189,7199,7211,7215,7219],{"__ignoreMap":77},[81,6796,6797,6799,6801,6803,6805,6807],{"class":83,"line":84},[81,6798,736],{"class":127},[81,6800,3689],{"class":127},[81,6802,3692],{"class":137},[81,6804,742],{"class":127},[81,6806,3697],{"class":91},[81,6808,144],{"class":137},[81,6810,6811,6813,6816,6818,6820],{"class":83,"line":147},[81,6812,736],{"class":127},[81,6814,6815],{"class":137}," { getEntryBySlug } ",[81,6817,742],{"class":127},[81,6819,6684],{"class":91},[81,6821,144],{"class":137},[81,6823,6824,6826,6828,6830,6833],{"class":83,"line":154},[81,6825,736],{"class":127},[81,6827,3944],{"class":137},[81,6829,742],{"class":127},[81,6831,6832],{"class":91}," \"node:fs\"",[81,6834,144],{"class":137},[81,6836,6837,6839,6841,6843,6845],{"class":83,"line":169},[81,6838,736],{"class":127},[81,6840,3765],{"class":137},[81,6842,742],{"class":127},[81,6844,3770],{"class":91},[81,6846,144],{"class":137},[81,6848,6849,6851,6853,6855,6857],{"class":83,"line":187},[81,6850,736],{"class":127},[81,6852,4260],{"class":137},[81,6854,742],{"class":127},[81,6856,4265],{"class":91},[81,6858,144],{"class":137},[81,6860,6861,6863,6865,6867,6869],{"class":83,"line":195},[81,6862,736],{"class":127},[81,6864,4630],{"class":137},[81,6866,742],{"class":127},[81,6868,4635],{"class":91},[81,6870,144],{"class":137},[81,6872,6873],{"class":83,"line":201},[81,6874,151],{"emptyLinePlaceholder":150},[81,6876,6877,6879,6881,6883,6885,6887,6889,6891,6893,6895],{"class":83,"line":206},[81,6878,3708],{"class":127},[81,6880,3711],{"class":127},[81,6882,3714],{"class":127},[81,6884,3717],{"class":87},[81,6886,3720],{"class":137},[81,6888,3723],{"class":547},[81,6890,3726],{"class":137},[81,6892,115],{"class":127},[81,6894,3731],{"class":87},[81,6896,3799],{"class":137},[81,6898,6899,6901,6903,6905,6907,6909],{"class":83,"line":223},[81,6900,3804],{"class":127},[81,6902,5492],{"class":137},[81,6904,5562],{"class":95},[81,6906,5498],{"class":137},[81,6908,5078],{"class":127},[81,6910,5616],{"class":137},[81,6912,6913],{"class":83,"line":229},[81,6914,6915],{"class":768},"  \u002F\u002F\u002F One line to get the post from our collection using slug\n",[81,6917,6918,6920,6922,6924,6926,6929,6931,6934,6937,6940],{"class":83,"line":235},[81,6919,3804],{"class":127},[81,6921,5883],{"class":95},[81,6923,134],{"class":127},[81,6925,4116],{"class":127},[81,6927,6928],{"class":87}," getEntryBySlug",[81,6930,178],{"class":137},[81,6932,6933],{"class":91},"\"post\"",[81,6935,6936],{"class":137},", slug",[81,6938,6939],{"class":127},"!!",[81,6941,184],{"class":137},[81,6943,6944,6946,6949,6951,6954,6957,6960],{"class":83,"line":243},[81,6945,3804],{"class":127},[81,6947,6948],{"class":95}," title",[81,6950,134],{"class":127},[81,6952,6953],{"class":137}," post?.data.title ",[81,6955,6956],{"class":127},"??",[81,6958,6959],{"class":91}," \"My Post\"",[81,6961,144],{"class":137},[81,6963,6964],{"class":83,"line":249},[81,6965,151],{"emptyLinePlaceholder":150},[81,6967,6968,6970,6972,6974,6976,6978,6980,6982,6984,6986],{"class":83,"line":266},[81,6969,3804],{"class":127},[81,6971,4060],{"class":95},[81,6973,134],{"class":127},[81,6975,4065],{"class":91},[81,6977,4068],{"class":137},[81,6979,212],{"class":91},[81,6981,4073],{"class":87},[81,6983,4076],{"class":91},[81,6985,4079],{"class":91},[81,6987,144],{"class":137},[81,6989,6990,6992,6994,6996,6998],{"class":83,"line":590},[81,6991,3804],{"class":127},[81,6993,4092],{"class":95},[81,6995,134],{"class":127},[81,6997,4097],{"class":87},[81,6999,4100],{"class":137},[81,7001,7002],{"class":83,"line":596},[81,7003,151],{"emptyLinePlaceholder":150},[81,7005,7006,7008,7010,7012,7014,7016],{"class":83,"line":606},[81,7007,3804],{"class":127},[81,7009,3807],{"class":95},[81,7011,134],{"class":127},[81,7013,3812],{"class":87},[81,7015,178],{"class":137},[81,7017,7018],{"class":91},"`\u003Cdiv\n",[81,7020,7021],{"class":83,"line":1519},[81,7022,3827],{"class":91},[81,7024,7025],{"class":83,"line":1530},[81,7026,3832],{"class":91},[81,7028,7029],{"class":83,"line":1536},[81,7030,3837],{"class":91},[81,7032,7033],{"class":83,"line":1541},[81,7034,3842],{"class":91},[81,7036,7037],{"class":83,"line":1546},[81,7038,3847],{"class":91},[81,7040,7041,7043,7045],{"class":83,"line":1552},[81,7042,6443],{"class":91},[81,7044,5300],{"class":137},[81,7046,198],{"class":91},[81,7048,7049],{"class":83,"line":1561},[81,7050,3882],{"class":91},[81,7052,7053,7055],{"class":83,"line":1566},[81,7054,3887],{"class":91},[81,7056,184],{"class":137},[81,7058,7059,7061,7063,7065,7067,7069],{"class":83,"line":1572},[81,7060,3804],{"class":127},[81,7062,4111],{"class":95},[81,7064,134],{"class":127},[81,7066,4116],{"class":127},[81,7068,3637],{"class":87},[81,7070,4121],{"class":137},[81,7072,7073,7075,7077],{"class":83,"line":1578},[81,7074,4126],{"class":137},[81,7076,4129],{"class":95},[81,7078,44],{"class":137},[81,7080,7081,7083,7085],{"class":83,"line":1584},[81,7082,4136],{"class":137},[81,7084,4139],{"class":95},[81,7086,44],{"class":137},[81,7088,7089],{"class":83,"line":1601},[81,7090,4146],{"class":137},[81,7092,7093],{"class":83,"line":1617},[81,7094,4151],{"class":137},[81,7096,7097,7099,7101],{"class":83,"line":1648},[81,7098,4156],{"class":137},[81,7100,4159],{"class":91},[81,7102,44],{"class":137},[81,7104,7105],{"class":83,"line":1653},[81,7106,4166],{"class":137},[81,7108,7109,7111,7113],{"class":83,"line":1663},[81,7110,4171],{"class":137},[81,7112,4174],{"class":91},[81,7114,44],{"class":137},[81,7116,7117],{"class":83,"line":1673},[81,7118,4181],{"class":137},[81,7120,7121],{"class":83,"line":1678},[81,7122,4186],{"class":137},[81,7124,7125],{"class":83,"line":1683},[81,7126,4191],{"class":137},[81,7128,7129,7131,7133,7135,7137,7139,7141,7143,7145],{"class":83,"line":1688},[81,7130,3804],{"class":127},[81,7132,4675],{"class":95},[81,7134,134],{"class":127},[81,7136,4680],{"class":87},[81,7138,4683],{"class":137},[81,7140,742],{"class":87},[81,7142,4688],{"class":137},[81,7144,4691],{"class":87},[81,7146,759],{"class":137},[81,7148,7149,7151,7153,7155,7157,7159,7161],{"class":83,"line":1694},[81,7150,3804],{"class":127},[81,7152,4700],{"class":95},[81,7154,134],{"class":127},[81,7156,4116],{"class":127},[81,7158,4707],{"class":137},[81,7160,4710],{"class":87},[81,7162,759],{"class":137},[81,7164,7165],{"class":83,"line":3041},[81,7166,151],{"emptyLinePlaceholder":150},[81,7168,7169,7171,7173,7175],{"class":83,"line":3047},[81,7170,190],{"class":127},[81,7172,1500],{"class":127},[81,7174,4504],{"class":87},[81,7176,4819],{"class":137},[81,7178,7179,7181,7183],{"class":83,"line":3053},[81,7180,4512],{"class":137},[81,7182,4515],{"class":95},[81,7184,44],{"class":137},[81,7186,7187],{"class":83,"line":3059},[81,7188,4522],{"class":137},[81,7190,7191,7193,7195,7197],{"class":83,"line":3065},[81,7192,4527],{"class":91},[81,7194,343],{"class":137},[81,7196,4840],{"class":91},[81,7198,44],{"class":137},[81,7200,7201,7204,7206,7209],{"class":83,"line":3071},[81,7202,7203],{"class":91},"      \"Cache-Control\"",[81,7205,343],{"class":137},[81,7207,7208],{"class":91},"\"s-maxage=1, stale-while-revalidate=59\"",[81,7210,44],{"class":137},[81,7212,7213],{"class":83,"line":3077},[81,7214,4539],{"class":137},[81,7216,7217],{"class":83,"line":3083},[81,7218,4191],{"class":137},[81,7220,7221],{"class":83,"line":3089},[81,7222,269],{"class":137},[11,7224,7225,7226,7229],{},"There you go! On visiting ",[41,7227,7228],{},"\u002Fpost\u002Fhello.og"," we should see the same OG Image as\nbefore.",[11,7231,7232,7233,7236,7237,7239],{},"In our GET route we can also ",[41,7234,7235],{},"fetch"," external API based on the current ",[41,7238,5413],{},"\nparams and we can use that data in our html markup template.",[11,7241,7242,7243],{},"I've made this into a simple github repo if you want to check out the full\nsource: ",[15,7244,7245],{"href":7245,"rel":7246},"https:\u002F\u002Fgithub.com\u002Frumaan\u002Fastro-vercel-og",[19],[11,7248,1735],{},[1737,7250,7251,7258,7265],{},[1740,7252,7253],{},[15,7254,7257],{"href":7255,"rel":7256},"https:\u002F\u002Fogp.me\u002F",[19],"The Open Graph Protocol",[1740,7259,7260],{},[15,7261,7264],{"href":7262,"rel":7263},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FRequest",[19],"Request Web API",[1740,7266,7267],{},[15,7268,7270],{"href":3577,"rel":7269},[19],"Vercel OG Image",[1752,7272,7273],{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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 pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sIZOC, html code.shiki .sIZOC{--shiki-default:#79B8FF;--shiki-default-font-weight:bold}",{"title":77,"searchDepth":147,"depth":147,"links":7275},[7276,7277,7278,7279,7280,7281,7282],{"id":3614,"depth":147,"text":3615},{"id":3662,"depth":147,"text":3663},{"id":4581,"depth":147,"text":4582},{"id":4877,"depth":147,"text":4878},{"id":5048,"depth":147,"text":5049},{"id":5402,"depth":147,"text":5403},{"id":6636,"depth":147,"text":6637},"2022-11-15","Simple and quick way to generate OG images in your Astro Project",{},"\u002Fblog\u002Fopen-graph-images-using-satori","11 min read",{"title":3566,"description":7284},"blog\u002Fopen-graph-images-using-satori","e5nxiaCHGBYjlZzv18Px1-jH7R9iBcrBrQLIpdvbOm0",1787996725404]