[{"data":1,"prerenderedAt":2009},["ShallowReactive",2],{"page:\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites":3,"all-docs-nav":1472},{"id":4,"title":5,"body":6,"breadcrumb":1449,"dateModified":1456,"datePublished":1456,"description":1457,"extension":1458,"faq":1459,"meta":1465,"navigation":1466,"path":1467,"seo":1468,"slug":12,"stem":1469,"type":1470,"__hash__":1471},"content\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Findex.md","Serverless Functions for Static Sites",{"type":7,"value":8,"toc":1426},"minimark",[9,13,17,36,208,213,216,223,229,326,333,448,452,460,473,477,480,788,795,799,807,899,903,926,929,933,936,967,971,974,1065,1068,1072,1097,1101,1104,1156,1160,1163,1170,1174,1177,1258,1262,1309,1313,1330,1334,1339,1342,1346,1349,1353,1356,1360,1363,1367,1370,1374,1422],[10,11,5],"h1",{"id":12},"serverless-functions-for-static-sites",[14,15,16],"p",{},"Every static site eventually needs a small amount of code that runs on a server. A contact form needs somewhere to post to. A newsletter signup needs to call a mailing-list API without exposing its key. A partner portal needs to check who is logged in before serving a page. A pricing page wants to show the reader's currency. None of that justifies abandoning static pages, and none of it needs a traditional server. Serverless and edge functions — small handlers deployed alongside the static files — fill exactly that gap.",[14,18,19,20,25,26,30,31,35],{},"This topic covers the functions that static sites actually need: form handling, API proxies, authentication gates and light personalisation, on Cloudflare Workers, Netlify Functions and Vercel Functions. The principle throughout is to keep pages static and cached, and to confine code to the specific routes that need it. It sits inside ",[21,22,24],"a",{"href":23},"\u002Fproduction-ready-deployment-cicd-workflows\u002F","Production-Ready Deployment & CI\u002FCD Workflows"," and builds on the hosting choices in ",[21,27,29],{"href":28},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002F","Netlify vs Vercel Deployment Strategies"," and ",[21,32,34],{"href":33},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002F","Cloudflare Pages Edge Caching Setup",".",[37,38,39,204],"figure",{},[40,41,48,49,48,53,48,57,48,64,48,190],"svg",{"viewBox":42,"role":43,"ariaLabelledBy":44,"xmlns":47},"0 0 780 320","img",[45,46],"sfn-arch-title","sfn-arch-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[50,51,52],"title",{"id":45},"Static pages and functions side by side",[54,55,56],"desc",{"id":46},"A reader's browser requests pages, which are served from the CDN cache as static files. Separate requests to \u002Fapi\u002F routes go to functions: a form handler that writes to storage and sends email, an API proxy that adds a secret key and caches responses, and an auth gate that checks a session before serving protected static files. Pages never wait on functions to render.",[58,59],"rect",{"x":60,"y":60,"width":61,"height":62,"fill":63},"0","780","320","#ffffff",[65,66,68,69,68,77,68,87,68,93,68,99,68,107,68,112,68,116,68,122,68,126,68,129,68,133,68,136,68,140,68,147,68,152,68,154,68,157,68,159,68,162,48],"g",{"style":67},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[70,71,76],"text",{"x":72,"y":73,"fill":74,"style":75},"390","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Pages stay static; code runs only on the routes that need it",[58,78],{"x":79,"y":80,"width":81,"height":82,"rx":83,"fill":84,"stroke":85,"style":86},"30","130","140","60","10","#f8fafc","#d9e2ef","stroke-width:1.5px",[70,88,92],{"x":89,"y":90,"fill":74,"style":91},"100","156","font-weight:700;text-anchor:middle","browser",[70,94,98],{"x":89,"y":95,"fill":96,"style":97},"174","#556071","font-size:11px;text-anchor:middle","reader",[58,100],{"x":101,"y":102,"width":103,"height":82,"rx":83,"fill":104,"opacity":105,"stroke":106,"style":86},"230","50","200","#8ac926","0.18","#5a8a16",[70,108,111],{"x":109,"y":110,"fill":74,"style":91},"330","76","static pages",[70,113,115],{"x":109,"y":114,"fill":96,"style":97},"94","CDN cache, ~98% hits",[58,117],{"x":101,"y":81,"width":103,"height":118,"rx":119,"fill":120,"opacity":121,"stroke":120,"style":86},"40","8","#6a4c93","0.14",[70,123,125],{"x":109,"y":124,"fill":74,"style":91},"165","POST \u002Fapi\u002Fcontact",[58,127],{"x":101,"y":128,"width":103,"height":118,"rx":119,"fill":120,"opacity":121,"stroke":120,"style":86},"192",[70,130,132],{"x":109,"y":131,"fill":74,"style":91},"217","GET \u002Fapi\u002Freleases",[58,134],{"x":101,"y":135,"width":103,"height":118,"rx":119,"fill":120,"opacity":121,"stroke":120,"style":86},"244",[70,137,139],{"x":109,"y":138,"fill":74,"style":91},"269","\u002Fpartners\u002F* (gated)",[58,141],{"x":142,"y":81,"width":143,"height":118,"rx":119,"fill":144,"opacity":145,"stroke":146,"style":86},"490","260","#ffca3a","0.24","#a97b00",[70,148,151],{"x":149,"y":124,"fill":74,"style":150},"620","text-anchor:middle","validate → store → email",[58,153],{"x":142,"y":128,"width":143,"height":118,"rx":119,"fill":144,"opacity":145,"stroke":146,"style":86},[70,155,156],{"x":149,"y":131,"fill":74,"style":150},"add secret key → cache 5 min",[58,158],{"x":142,"y":135,"width":143,"height":118,"rx":119,"fill":144,"opacity":145,"stroke":146,"style":86},[70,160,161],{"x":149,"y":138,"fill":74,"style":150},"check session → serve static file",[65,163,166,167,166,172,166,175,166,178,166,181,166,184,166,187,68],{"stroke":96,"fill":164,"style":165},"none","stroke-width:2px","\n      ",[168,169],"path",{"d":170,"style":171},"M172 150 L226 88","marker-end:url(#sfn-arrow)",[168,173],{"d":174,"style":171},"M172 160 L226 160",[168,176],{"d":177,"style":171},"M172 172 L226 210",[168,179],{"d":180,"style":171},"M172 180 L226 260",[168,182],{"d":183,"style":171},"M432 160 L486 160",[168,185],{"d":186,"style":171},"M432 212 L486 212",[168,188],{"d":189,"style":171},"M432 264 L486 264",[191,192,68,193,48],"defs",{},[194,195,166,201,68],"marker",{"id":196,"viewBox":197,"refX":119,"refY":198,"markerWidth":199,"markerHeight":199,"orient":200},"sfn-arrow","0 0 10 10","5","7","auto-start-reverse",[168,202],{"d":203,"fill":96},"M0 0 L10 5 L0 10 z",[205,206,207],"figcaption",{},"The page request never touches a function; functions answer separate, specific requests.",[209,210,212],"h2",{"id":211},"edge-functions-versus-regional-functions","Edge Functions Versus Regional Functions",[14,214,215],{},"The platforms offer two execution models, and the right one depends on what the code does.",[14,217,218,222],{},[219,220,221],"strong",{},"Edge functions"," — Cloudflare Workers, Netlify Edge Functions, Vercel Edge Functions — run in V8 isolates at hundreds of locations. Cold starts are measured in single-digit milliseconds, they run close to the reader, and they are billed per request. They have tighter limits: CPU time per request in the tens of milliseconds on free plans, a subset of Node.js APIs, and no long-running work. They suit request routing, header manipulation, redirects, lightweight APIs, auth checks and proxies.",[14,224,225,228],{},[219,226,227],{},"Regional functions"," — Netlify Functions and Vercel Functions on the Node.js runtime, AWS Lambda — run full Node.js in one or a few regions. Cold starts range from 100 ms to over a second, and they sit far from some readers, but they allow longer execution, larger dependencies and native modules. They suit heavy work such as image processing, PDF generation, or anything that talks to a database in a specific region.",[230,231,232,254],"table",{},[233,234,235],"thead",{},[236,237,238,242,245,248,251],"tr",{},[239,240,241],"th",{},"Measured on a contact form endpoint",[239,243,244],{},"Cloudflare Worker",[239,246,247],{},"Netlify Edge Function",[239,249,250],{},"Netlify Function (Node)",[239,252,253],{},"Vercel Function (Node)",[255,256,257,275,292,309],"tbody",{},[236,258,259,263,266,269,272],{},[260,261,262],"td",{},"Cold start (p50)",[260,264,265],{},"4 ms",[260,267,268],{},"11 ms",[260,270,271],{},"290 ms",[260,273,274],{},"240 ms",[236,276,277,280,283,286,289],{},[260,278,279],{},"Warm response, reader in Frankfurt (p50)",[260,281,282],{},"22 ms",[260,284,285],{},"31 ms",[260,287,288],{},"58 ms (region eu-central)",[260,290,291],{},"64 ms (fra1)",[236,293,294,297,300,303,306],{},[260,295,296],{},"Warm response, reader in Sydney (p50)",[260,298,299],{},"26 ms",[260,301,302],{},"38 ms",[260,304,305],{},"312 ms",[260,307,308],{},"330 ms",[236,310,311,314,317,320,323],{},[260,312,313],{},"CPU limit per request (default)",[260,315,316],{},"10 ms free \u002F 30 s paid",[260,318,319],{},"50 ms",[260,321,322],{},"10 s",[260,324,325],{},"10–60 s",[14,327,328,329,35],{},"For the functions static sites typically need, edge functions are the better default; regional functions are for the exceptions. The comparison is expanded in ",[21,330,332],{"href":331},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers\u002F","Netlify Functions vs Cloudflare Workers",[37,334,335,445],{},[40,336,48,341,48,344,48,347,48,350],{"viewBox":337,"role":43,"ariaLabelledBy":338,"xmlns":47},"0 0 780 280",[339,340],"sfn-lat-title","sfn-lat-desc",[50,342,343],{"id":339},"Warm response time by reader location",[54,345,346],{"id":340},"Paired bars for a reader in Frankfurt and one in Sydney. The Cloudflare Worker answers in 22 and 26 milliseconds. The Netlify Edge Function in 31 and 38. The Netlify Node function in eu-central answers in 58 from Frankfurt but 312 from Sydney. The Vercel Node function in fra1 answers in 64 and 330.",[58,348],{"x":60,"y":60,"width":61,"height":349,"fill":63},"280",[65,351,68,352,68,355,68,360,68,364,68,372,68,376,68,382,68,386,68,389,68,393,68,398,68,402,68,406,68,410,68,414,68,418,68,422,68,426,68,432,68,435,68,439,68,441,48],{"style":67},[70,353,354],{"x":72,"y":73,"fill":74,"style":75},"Edge stays flat across the world; regional does not",[356,357],"line",{"x1":101,"y1":358,"x2":101,"y2":359,"stroke":85,"style":86},"46","236",[70,361,244],{"x":79,"y":362,"fill":74,"style":363},"70","font-weight:700",[58,365],{"x":101,"y":366,"width":367,"height":368,"fill":369,"opacity":370,"stroke":369,"style":371},"58","33","12","#1982c4","0.3","stroke-width:1px",[58,373],{"x":101,"y":374,"width":375,"height":368,"fill":120,"opacity":370,"stroke":120,"style":371},"72","39",[70,377,381],{"x":378,"y":379,"fill":74,"style":380},"277","82","font-size:11px","22 \u002F 26 ms",[70,383,385],{"x":79,"y":384,"fill":74,"style":363},"116","Netlify Edge",[58,387],{"x":101,"y":388,"width":358,"height":368,"fill":369,"opacity":370,"stroke":369,"style":371},"104",[58,390],{"x":101,"y":391,"width":392,"height":368,"fill":120,"opacity":370,"stroke":120,"style":371},"118","57",[70,394,397],{"x":395,"y":396,"fill":74,"style":380},"295","128","31 \u002F 38 ms",[70,399,401],{"x":79,"y":400,"fill":74,"style":363},"162","Netlify Function",[58,403],{"x":101,"y":404,"width":405,"height":368,"fill":369,"opacity":370,"stroke":369,"style":371},"150","87",[58,407],{"x":101,"y":408,"width":409,"height":368,"fill":120,"opacity":370,"stroke":120,"style":371},"164","468",[70,411,413],{"x":412,"y":95,"fill":74,"style":380},"706","58 \u002F 312",[70,415,417],{"x":79,"y":416,"fill":74,"style":363},"208","Vercel Function",[58,419],{"x":101,"y":420,"width":421,"height":368,"fill":369,"opacity":370,"stroke":369,"style":371},"196","96",[58,423],{"x":101,"y":424,"width":425,"height":368,"fill":120,"opacity":370,"stroke":120,"style":371},"210","495",[70,427,431],{"x":428,"y":429,"fill":74,"style":430},"700","248","font-size:11px;text-anchor:end","64 \u002F 330 ms",[58,433],{"x":101,"y":434,"width":368,"height":368,"fill":369,"opacity":370,"stroke":369,"style":371},"256",[70,436,438],{"x":429,"y":437,"fill":96,"style":380},"266","Frankfurt",[58,440],{"x":109,"y":434,"width":368,"height":368,"fill":120,"opacity":370,"stroke":120,"style":371},[70,442,444],{"x":443,"y":437,"fill":96,"style":380},"348","Sydney",[205,446,447],{},"For a global audience, where the function runs matters more than how fast the code is.",[209,449,451],{"id":450},"forms-without-a-form-service","Forms Without a Form Service",[14,453,454,455,459],{},"The most common function on a static site is a form handler. It receives a POST, validates the fields, blocks spam, stores or forwards the submission and redirects the reader to a thank-you page. A hosted form service does this with no code; a function does it with full control over validation, storage and data location. The progressive-enhancement version works without JavaScript — an ordinary ",[456,457,458],"code",{},"\u003Cform method=\"post\" action=\"\u002Fapi\u002Fcontact\">"," — and a small script can upgrade it to an inline success message.",[14,461,462,463,467,468,472],{},"Spam protection matters more than anything else in the handler. A honeypot field and a minimum time-to-submit check stop most bots; a challenge such as Cloudflare Turnstile stops the rest without puzzles for readers. ",[21,464,466],{"href":465},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fhandling-form-submissions-on-a-static-site\u002F","Handling Form Submissions on a Static Site"," compares approaches, and ",[21,469,471],{"href":470},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fadding-a-contact-form-with-cloudflare-workers\u002F","Adding a Contact Form with Cloudflare Workers"," builds one end to end.",[209,474,476],{"id":475},"proxying-third-party-apis","Proxying Third-Party APIs",[14,478,479],{},"Static pages often want live data: the latest release from GitHub, stock levels, a status summary, search from a hosted index with a private key. Calling those APIs directly from the browser either exposes a secret key or runs into CORS and rate limits. An edge function in between adds the key server-side, restricts which upstream endpoints can be called, caches responses for a sensible period, and shapes the response to exactly what the page needs.",[481,482,487],"pre",{"className":483,"code":484,"language":485,"meta":486,"style":486},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F functions\u002Fapi\u002Freleases.js (Cloudflare Pages Function)\nexport async function onRequestGet({ env }) {\n  const cache = caches.default, key = new Request('https:\u002F\u002Fcache.internal\u002Freleases');\n  let res = await cache.match(key);\n  if (!res) {\n    const up = await fetch('https:\u002F\u002Fapi.github.com\u002Frepos\u002Facme\u002Fcli\u002Freleases?per_page=5', {\n      headers: { authorization: `Bearer ${env.GH_TOKEN}`, 'user-agent': 'docs-site' } });\n    const data = (await up.json()).map((r) => ({ tag: r.tag_name, date: r.published_at, url: r.html_url }));\n    res = new Response(JSON.stringify(data), { headers: { 'content-type': 'application\u002Fjson', 'cache-control': 'public, max-age=300' } });\n    await cache.put(key, res.clone());\n  }\n  return res;\n}\n","js","",[456,488,489,497,525,562,586,601,625,659,701,747,767,773,782],{"__ignoreMap":486},[490,491,493],"span",{"class":356,"line":492},1,[490,494,496],{"class":495},"sJ8bj","\u002F\u002F functions\u002Fapi\u002Freleases.js (Cloudflare Pages Function)\n",[490,498,500,504,507,510,514,518,522],{"class":356,"line":499},2,[490,501,503],{"class":502},"szBVR","export",[490,505,506],{"class":502}," async",[490,508,509],{"class":502}," function",[490,511,513],{"class":512},"sScJk"," onRequestGet",[490,515,517],{"class":516},"sVt8B","({ ",[490,519,521],{"class":520},"s4XuR","env",[490,523,524],{"class":516}," }) {\n",[490,526,528,531,535,538,541,544,546,549,552,555,559],{"class":356,"line":527},3,[490,529,530],{"class":502},"  const",[490,532,534],{"class":533},"sj4cs"," cache",[490,536,537],{"class":502}," =",[490,539,540],{"class":516}," caches.default, ",[490,542,543],{"class":533},"key",[490,545,537],{"class":502},[490,547,548],{"class":502}," new",[490,550,551],{"class":512}," Request",[490,553,554],{"class":516},"(",[490,556,558],{"class":557},"sZZnC","'https:\u002F\u002Fcache.internal\u002Freleases'",[490,560,561],{"class":516},");\n",[490,563,565,568,571,574,577,580,583],{"class":356,"line":564},4,[490,566,567],{"class":502},"  let",[490,569,570],{"class":516}," res ",[490,572,573],{"class":502},"=",[490,575,576],{"class":502}," await",[490,578,579],{"class":516}," cache.",[490,581,582],{"class":512},"match",[490,584,585],{"class":516},"(key);\n",[490,587,589,592,595,598],{"class":356,"line":588},5,[490,590,591],{"class":502},"  if",[490,593,594],{"class":516}," (",[490,596,597],{"class":502},"!",[490,599,600],{"class":516},"res) {\n",[490,602,604,607,610,612,614,617,619,622],{"class":356,"line":603},6,[490,605,606],{"class":502},"    const",[490,608,609],{"class":533}," up",[490,611,537],{"class":502},[490,613,576],{"class":502},[490,615,616],{"class":512}," fetch",[490,618,554],{"class":516},[490,620,621],{"class":557},"'https:\u002F\u002Fapi.github.com\u002Frepos\u002Facme\u002Fcli\u002Freleases?per_page=5'",[490,623,624],{"class":516},", {\n",[490,626,628,631,634,636,638,641,644,647,650,653,656],{"class":356,"line":627},7,[490,629,630],{"class":516},"      headers: { authorization: ",[490,632,633],{"class":557},"`Bearer ${",[490,635,521],{"class":516},[490,637,35],{"class":557},[490,639,640],{"class":533},"GH_TOKEN",[490,642,643],{"class":557},"}`",[490,645,646],{"class":516},", ",[490,648,649],{"class":557},"'user-agent'",[490,651,652],{"class":516},": ",[490,654,655],{"class":557},"'docs-site'",[490,657,658],{"class":516}," } });\n",[490,660,662,664,667,669,671,674,677,680,683,686,689,692,695,698],{"class":356,"line":661},8,[490,663,606],{"class":502},[490,665,666],{"class":533}," data",[490,668,537],{"class":502},[490,670,594],{"class":516},[490,672,673],{"class":502},"await",[490,675,676],{"class":516}," up.",[490,678,679],{"class":512},"json",[490,681,682],{"class":516},"()).",[490,684,685],{"class":512},"map",[490,687,688],{"class":516},"((",[490,690,691],{"class":520},"r",[490,693,694],{"class":516},") ",[490,696,697],{"class":502},"=>",[490,699,700],{"class":516}," ({ tag: r.tag_name, date: r.published_at, url: r.html_url }));\n",[490,702,704,707,709,711,714,716,719,721,724,727,730,732,735,737,740,742,745],{"class":356,"line":703},9,[490,705,706],{"class":516},"    res ",[490,708,573],{"class":502},[490,710,548],{"class":502},[490,712,713],{"class":512}," Response",[490,715,554],{"class":516},[490,717,718],{"class":533},"JSON",[490,720,35],{"class":516},[490,722,723],{"class":512},"stringify",[490,725,726],{"class":516},"(data), { headers: { ",[490,728,729],{"class":557},"'content-type'",[490,731,652],{"class":516},[490,733,734],{"class":557},"'application\u002Fjson'",[490,736,646],{"class":516},[490,738,739],{"class":557},"'cache-control'",[490,741,652],{"class":516},[490,743,744],{"class":557},"'public, max-age=300'",[490,746,658],{"class":516},[490,748,750,753,755,758,761,764],{"class":356,"line":749},10,[490,751,752],{"class":502},"    await",[490,754,579],{"class":516},[490,756,757],{"class":512},"put",[490,759,760],{"class":516},"(key, res.",[490,762,763],{"class":512},"clone",[490,765,766],{"class":516},"());\n",[490,768,770],{"class":356,"line":769},11,[490,771,772],{"class":516},"  }\n",[490,774,776,779],{"class":356,"line":775},12,[490,777,778],{"class":502},"  return",[490,780,781],{"class":516}," res;\n",[490,783,785],{"class":356,"line":784},13,[490,786,787],{"class":516},"}\n",[14,789,790,791,35],{},"With a five-minute cache, 1.2 million page views a month produced about 9,000 upstream calls, comfortably within GitHub's rate limit. The full pattern, including allow-lists and error handling, is in ",[21,792,794],{"href":793},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fproxying-third-party-apis-from-an-edge-function\u002F","Proxying Third-Party APIs from an Edge Function",[209,796,798],{"id":797},"gating-static-content-behind-authentication","Gating Static Content Behind Authentication",[14,800,801,802,806],{},"Partner portals, internal handbooks and paid documentation are often perfect static sites that must not be public. An edge function in front of the static assets can check a session cookie or an identity provider's token on every request and serve the static file only when the check passes — without turning the site into an application. On Cloudflare, Access does this with no code; elsewhere a middleware function verifies a signed JWT from the identity provider. ",[21,803,805],{"href":804},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication\u002F","Protecting a Static Site Behind Authentication"," covers both, including how to keep protected pages out of CDN caches shared between users.",[37,808,809,896],{},[40,810,48,815,48,818,48,821,48,824,48,889],{"viewBox":811,"role":43,"ariaLabelledBy":812,"xmlns":47},"0 0 780 270",[813,814],"sfn-gate-title","sfn-gate-desc",[50,816,817],{"id":813},"Authentication gate in front of static files",[54,819,820],{"id":814},"A request for a protected page hits an edge function. If the session token is valid, the function fetches the static file and returns it with private cache headers. If not, it redirects to the identity provider's login page. After login, the provider redirects back with a token and the function sets a session cookie.",[58,822],{"x":60,"y":60,"width":61,"height":823,"fill":63},"270",[65,825,68,826,68,829,68,832,68,836,68,840,68,844,68,848,68,851,68,855,68,859,68,863,68,866,68,870,68,873,68,885,48],{"style":67},[70,827,828],{"x":72,"y":73,"fill":74,"style":75},"The pages are still static; only access is checked per request",[58,830],{"x":79,"y":89,"width":81,"height":831,"rx":83,"fill":84,"stroke":85,"style":86},"56",[70,833,835],{"x":89,"y":834,"fill":74,"style":91},"124","GET \u002Fpartners\u002F…",[70,837,839],{"x":89,"y":838,"fill":96,"style":97},"142","with cookie?",[58,841],{"x":842,"y":89,"width":843,"height":831,"rx":83,"fill":120,"opacity":121,"stroke":120,"style":86},"220","170",[70,845,847],{"x":846,"y":834,"fill":74,"style":91},"305","edge function",[70,849,850],{"x":846,"y":838,"fill":96,"style":97},"verify JWT (~1 ms)",[58,852],{"x":853,"y":102,"width":854,"height":102,"rx":83,"fill":104,"opacity":105,"stroke":106,"style":86},"450","290",[70,856,858],{"x":857,"y":374,"fill":74,"style":91},"595","valid → serve static file",[70,860,862],{"x":857,"y":861,"fill":96,"style":97},"90","Cache-Control: private",[58,864],{"x":853,"y":865,"width":854,"height":102,"rx":83,"fill":144,"opacity":145,"stroke":146,"style":86},"160",[70,867,869],{"x":857,"y":868,"fill":74,"style":91},"182","missing \u002F invalid → 302 to IdP",[70,871,872],{"x":857,"y":103,"fill":96,"style":97},"return with token, set cookie",[65,874,166,875,166,879,166,882,68],{"stroke":96,"fill":164,"style":165},[168,876],{"d":877,"style":878},"M172 128 L216 128","marker-end:url(#sfn-g-arrow)",[168,880],{"d":881,"style":878},"M392 118 L446 80",[168,883],{"d":884,"style":878},"M392 138 L446 180",[70,886,888],{"x":72,"y":887,"fill":96,"style":97},"250","Public pages on the same site bypass the function entirely and stay in the shared cache",[191,890,68,891,48],{},[194,892,166,894,68],{"id":893,"viewBox":197,"refX":119,"refY":198,"markerWidth":199,"markerHeight":199,"orient":200},"sfn-g-arrow",[168,895],{"d":203,"fill":96},[205,897,898],{},"Private cache headers on protected responses are the detail that keeps one reader's page out of another's CDN cache hit.",[209,900,902],{"id":901},"light-personalisation-without-losing-the-cache","Light Personalisation Without Losing the Cache",[14,904,905,906,909,910,913,914,917,918,921,922,925],{},"Personalisation is where static sites most often go wrong with functions: rendering every page per request to show a greeting throws away the CDN cache for a trivial benefit. Three patterns keep pages cacheable. ",[219,907,908],{},"Client-side islands"," fetch a small personalised fragment from a function after load — a \"welcome back\" line, a currency-converted price — while the page itself stays static. ",[219,911,912],{},"Edge variants"," serve one of a small number of pre-built pages based on a request attribute, such as ",[456,915,916],{},"\u002Fpricing\u002Feu\u002F"," versus ",[456,919,920],{},"\u002Fpricing\u002Fus\u002F"," chosen by country, with each variant cached separately. ",[219,923,924],{},"Header injection"," uses an edge function or HTMLRewriter to modify a cached page in flight — inserting the reader's country into a data attribute — without regenerating it.",[14,927,928],{},"On a marketing site that replaced per-request rendering of the pricing page with two edge variants, edge cache hit ratio for that page rose from 0% to 96%, and TTFB p75 fell from 410 ms to 38 ms.",[209,930,932],{"id":931},"webhooks-and-build-triggers","Webhooks and Build Triggers",[14,934,935],{},"Not every function faces readers. A second family receives webhooks from other systems and turns them into actions on the static site: a headless CMS publishes an entry and a function triggers a rebuild; a GitHub release is published and a function purges the cached releases widget; a payment provider confirms a purchase and a function adds the buyer to a partner allow-list. These functions are small, but they are the glue that makes a static site feel live.",[14,937,938,939,942,943,946,947,950,951,954,955,958,959,30,963,35],{},"Three rules keep them reliable. ",[219,940,941],{},"Verify signatures",": every serious webhook sender signs its payload with a shared secret (GitHub's ",[456,944,945],{},"X-Hub-Signature-256",", Stripe's ",[456,948,949],{},"Stripe-Signature",", most CMSs' equivalents); reject anything that does not verify, or a public URL becomes a way for anyone to trigger rebuilds. ",[219,952,953],{},"Respond fast, work later",": acknowledge within a second or two and hand the work to a queue or a build hook, because senders retry on timeouts and a slow handler produces duplicate builds. ",[219,956,957],{},"Debounce",": a CMS editor saving ten times in a minute should produce one rebuild, not ten — a short delay window in a KV entry or Durable Object collapses bursts. The build side of this is covered in ",[21,960,962],{"href":961},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates\u002F","Netlify Build Hooks for Content Updates",[21,964,966],{"href":965},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build\u002F","Wiring a Headless CMS to a Static Build",[209,968,970],{"id":969},"where-functions-live-in-each-stack","Where Functions Live in Each Stack",[14,972,973],{},"Every major static host now accepts functions in the same repository as the site, deployed atomically with it:",[230,975,976,992],{},[233,977,978],{},[236,979,980,983,986,989],{},[239,981,982],{},"Host",[239,984,985],{},"Location in repo",[239,987,988],{},"Runtime",[239,990,991],{},"Routing",[255,993,994,1011,1036,1051],{},[236,995,996,999,1005,1008],{},[260,997,998],{},"Cloudflare Pages \u002F Workers",[260,1000,1001,1004],{},[456,1002,1003],{},"functions\u002F"," (Pages) or a Worker with static assets",[260,1006,1007],{},"V8 isolate",[260,1009,1010],{},"file path = route",[236,1012,1013,1016,1024,1027],{},[260,1014,1015],{},"Netlify",[260,1017,1018,646,1021],{},[456,1019,1020],{},"netlify\u002Ffunctions\u002F",[456,1022,1023],{},"netlify\u002Fedge-functions\u002F",[260,1025,1026],{},"Node or Deno (edge)",[260,1028,1029,1032,1033,1035],{},[456,1030,1031],{},"\u002F.netlify\u002Ffunctions\u002F*"," or ",[456,1034,168],{}," config",[236,1037,1038,1041,1046,1049],{},[260,1039,1040],{},"Vercel",[260,1042,1043],{},[456,1044,1045],{},"api\u002F",[260,1047,1048],{},"Node or Edge",[260,1050,1010],{},[236,1052,1053,1056,1059,1062],{},[260,1054,1055],{},"AWS",[260,1057,1058],{},"separate Lambda \u002F CloudFront Function",[260,1060,1061],{},"Node",[260,1063,1064],{},"CloudFront behaviour or API Gateway",[14,1066,1067],{},"Keeping functions in the site's repository means the form handler and the form markup change in the same pull request, deploy together, and roll back together — the same atomic guarantee static files get. The generator does not matter: an Astro, Hugo or Eleventy build output sits next to the functions directory, and the host wires both up.",[209,1069,1071],{"id":1070},"testing-functions-locally-and-in-ci","Testing Functions Locally and in CI",[14,1073,1074,1075,1078,1079,1082,1083,1086,1087,646,1090,1032,1093,1096],{},"Functions deserve the same test discipline as the rest of the pipeline, and they are easier to test than most server code because each is a single function from request to response. Unit-test the handler by constructing a ",[456,1076,1077],{},"Request"," and asserting on the ",[456,1080,1081],{},"Response","; the Workers runtime, Netlify and Vercel all expose standard Fetch API objects, so tests run in plain Node or in ",[456,1084,1085],{},"vitest"," with the platform's test environment. Run the full local stack with ",[456,1088,1089],{},"wrangler pages dev",[456,1091,1092],{},"netlify dev",[456,1094,1095],{},"vercel dev"," to exercise routing alongside the built site. And add the function routes to the post-deploy smoke tests against every preview: a form endpoint should return 400 on an empty POST and 303 on a valid one, which proves both validation and routing without sending a real message.",[209,1098,1100],{"id":1099},"operating-functions-safely","Operating Functions Safely",[14,1102,1103],{},"Functions are server code, and bring server concerns:",[1105,1106,1107,1118,1124,1130,1140,1146],"ul",{},[1108,1109,1110,1113,1114,1117],"li",{},[219,1111,1112],{},"Secrets"," live in the platform's secret store (",[456,1115,1116],{},"wrangler secret put",", Netlify and Vercel environment variables), never in the repository or the built output.",[1108,1119,1120,1123],{},[219,1121,1122],{},"Rate limits"," on public endpoints: a form or proxy without one is an invitation to abuse. Cloudflare's rate-limiting rules or a counter in KV or Durable Objects cover it.",[1108,1125,1126,1129],{},[219,1127,1128],{},"Input validation"," for every field, with strict length limits and allow-lists rather than trying to block bad input.",[1108,1131,1132,1135,1136,35],{},[219,1133,1134],{},"Observability",": log function errors and latency the same way you monitor the rest of the site, as described in ",[21,1137,1139],{"href":1138},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002F","Monitoring Static Sites in Production",[1108,1141,1142,1145],{},[219,1143,1144],{},"Previews",": functions deploy with every preview, so they must not send real emails or write to production data from preview environments. Gate side effects on an environment variable.",[1108,1147,1148,1151,1152,1155],{},[219,1149,1150],{},"Timeouts to upstreams",": set an explicit timeout on every outbound ",[456,1153,1154],{},"fetch",", and return a graceful fallback, so a slow third party cannot hold a function — and the reader waiting on it — for the platform's maximum duration.",[209,1157,1159],{"id":1158},"cost-model","Cost Model",[14,1161,1162],{},"Function pricing is per request and per unit of compute, which makes it cheap for the traffic static sites actually send to functions — a small fraction of page views. On the site measured below, functions received about 1.4% of all requests: form submissions, the releases widget's cached API calls, and partner-page auth checks. At 1.2 million page views a month that was roughly 17,000 function invocations, well inside the free tiers of every platform listed above. The partner section was the exception, because the auth gate runs on every request to every protected page and asset; its 90,000 monthly requests pushed the site onto a paid Workers plan at 5 USD a month, still less than the VPS it replaced.",[14,1164,1165,1166,1169],{},"Two habits keep costs predictable. Route only the paths that need code through functions — on Cloudflare Pages, ",[456,1167,1168],{},"_routes.json"," excludes static paths from invoking functions at all; on Netlify and Vercel, keep functions under distinct paths rather than catch-all middleware. And cache function responses wherever the data allows, as in the releases proxy above, so repeated requests are answered from cache rather than from compute.",[209,1171,1173],{"id":1172},"measured-impact","Measured Impact",[14,1175,1176],{},"A documentation and marketing site moved four features from third-party embeds and a small VPS to edge functions on Cloudflare: the contact form, a newsletter signup, a GitHub releases widget and a partner-only section.",[230,1178,1179,1192],{},[233,1180,1181],{},[236,1182,1183,1186,1189],{},[239,1184,1185],{},"Measure",[239,1187,1188],{},"Before",[239,1190,1191],{},"After",[255,1193,1194,1204,1215,1226,1237,1247],{},[236,1195,1196,1199,1202],{},[260,1197,1198],{},"Third-party scripts on affected pages",[260,1200,1201],{},"3 (form embed, newsletter widget, GitHub badge)",[260,1203,60],{},[236,1205,1206,1209,1212],{},[260,1207,1208],{},"JavaScript on the contact page (compressed)",[260,1210,1211],{},"142 KB",[260,1213,1214],{},"3 KB",[236,1216,1217,1220,1223],{},[260,1218,1219],{},"Contact page LCP p75 (mobile)",[260,1221,1222],{},"2.9 s",[260,1224,1225],{},"1.4 s",[236,1227,1228,1231,1234],{},[260,1229,1230],{},"Spam submissions per week",[260,1232,1233],{},"~340",[260,1235,1236],{},"~2",[236,1238,1239,1242,1245],{},[260,1240,1241],{},"Servers to patch",[260,1243,1244],{},"1 VPS",[260,1246,60],{},[236,1248,1249,1252,1255],{},[260,1250,1251],{},"Monthly cost",[260,1253,1254],{},"24 USD (VPS + form service)",[260,1256,1257],{},"5 USD (Workers paid plan)",[209,1259,1261],{"id":1260},"common-pitfalls","Common Pitfalls",[1105,1263,1264,1270,1276,1282,1288,1294,1303],{},[1108,1265,1266,1269],{},[219,1267,1268],{},"Functions on the page-load path."," Rendering pages per request discards the CDN cache. Keep pages static; call functions after load or on submit.",[1108,1271,1272,1275],{},[219,1273,1274],{},"Regional functions for global audiences."," A function in one region adds hundreds of milliseconds for distant readers. Prefer edge unless you need regional capabilities.",[1108,1277,1278,1281],{},[219,1279,1280],{},"Secrets in client bundles."," Any key in front-end code is public. Proxy through a function.",[1108,1283,1284,1287],{},[219,1285,1286],{},"No rate limiting."," Public endpoints get abused within days of launch.",[1108,1289,1290,1293],{},[219,1291,1292],{},"Side effects in previews."," Preview deploys run the same functions; stub email and writes outside production.",[1108,1295,1296,1299,1300,1302],{},[219,1297,1298],{},"Caching personalised responses publicly."," Use ",[456,1301,862],{}," or vary the cache key correctly, or one reader's data reaches another.",[1108,1304,1305,1308],{},[219,1306,1307],{},"Catch-all middleware."," A function that runs on every path, including images and scripts, multiplies invocations and adds latency to assets that never needed code. Scope routes narrowly.",[209,1310,1312],{"id":1311},"key-takeaways","Key Takeaways",[1105,1314,1315,1318,1321,1324,1327],{},[1108,1316,1317],{},"Functions add the dynamic 5% a static site needs without giving up static pages or CDN caching.",[1108,1319,1320],{},"Edge functions start in milliseconds and respond in tens of milliseconds worldwide; regional functions are for heavier work.",[1108,1322,1323],{},"Forms, API proxies, auth gates and light personalisation cover almost every static-site use case.",[1108,1325,1326],{},"Keep functions off the page-load path; personalise with islands, edge variants or header injection.",[1108,1328,1329],{},"Treat functions as server code: secrets, rate limits, validation, monitoring and safe previews.",[209,1331,1333],{"id":1332},"faq","FAQ",[1335,1336,1338],"h3",{"id":1337},"does-adding-a-function-make-a-site-no-longer-static","Does adding a function make a site no longer static?",[14,1340,1341],{},"No. The pages stay pre-built files served from the CDN. A function handles only the specific routes that need to run code, such as a form endpoint or an API proxy, so the rest of the site keeps its static performance and simplicity.",[1335,1343,1345],{"id":1344},"edge-functions-or-regional-serverless-functions","Edge functions or regional serverless functions?",[14,1347,1348],{},"Edge functions run in many locations close to readers with fast cold starts and suit short request handling, redirects, headers and lightweight APIs. Regional functions run in one location with fuller Node.js support and longer limits, and suit heavier work or anything that needs a database in the same region.",[1335,1350,1352],{"id":1351},"what-are-the-most-common-uses-on-static-sites","What are the most common uses on static sites?",[14,1354,1355],{},"Contact and signup forms, proxying third-party APIs to hide keys, gating content behind authentication, lightweight personalisation such as geolocation, image or OG generation on demand, and webhooks that trigger rebuilds.",[1335,1357,1359],{"id":1358},"how-do-i-keep-functions-from-slowing-down-pages","How do I keep functions from slowing down pages?",[14,1361,1362],{},"Keep them off the page-load path. Pages should be static and cached; functions should answer separate requests made after load or on form submission. If a function must run on page requests, keep it at the edge and cache its output.",[1335,1364,1366],{"id":1365},"are-functions-a-security-risk-on-a-static-site","Are functions a security risk on a static site?",[14,1368,1369],{},"They add attack surface, so treat them like any server code. Validate input, rate-limit public endpoints, keep secrets in the platform's secret store, and give each function only the permissions it needs.",[209,1371,1373],{"id":1372},"related","Related",[1105,1375,1376,1385,1390,1395,1400,1405,1410,1417],{},[1108,1377,1378,1381,1382,1384],{},[219,1379,1380],{},"Up:"," ",[21,1383,24],{"href":23}," — where functions fit in the deploy pipeline.",[1108,1386,1387,1389],{},[21,1388,466],{"href":465}," — options from hosted services to your own function.",[1108,1391,1392,1394],{},[21,1393,471],{"href":470}," — an end-to-end build.",[1108,1396,1397,1399],{},[21,1398,332],{"href":331}," — choosing a platform.",[1108,1401,1402,1404],{},[21,1403,794],{"href":793}," — hiding keys and caching responses.",[1108,1406,1407,1409],{},[21,1408,805],{"href":804}," — gating static content.",[1108,1411,1412,1416],{},[21,1413,1415],{"href":1414},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs\u002F","Vercel ISR vs Static Generation for SSGs"," — when regeneration beats functions.",[1108,1418,1419,1421],{},[21,1420,962],{"href":961}," — functions that trigger rebuilds.",[1423,1424,1425],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":486,"searchDepth":499,"depth":499,"links":1427},[1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1448],{"id":211,"depth":499,"text":212},{"id":450,"depth":499,"text":451},{"id":475,"depth":499,"text":476},{"id":797,"depth":499,"text":798},{"id":901,"depth":499,"text":902},{"id":931,"depth":499,"text":932},{"id":969,"depth":499,"text":970},{"id":1070,"depth":499,"text":1071},{"id":1099,"depth":499,"text":1100},{"id":1158,"depth":499,"text":1159},{"id":1172,"depth":499,"text":1173},{"id":1260,"depth":499,"text":1261},{"id":1311,"depth":499,"text":1312},{"id":1332,"depth":499,"text":1333,"children":1442},[1443,1444,1445,1446,1447],{"id":1337,"depth":527,"text":1338},{"id":1344,"depth":527,"text":1345},{"id":1351,"depth":527,"text":1352},{"id":1358,"depth":527,"text":1359},{"id":1365,"depth":527,"text":1366},{"id":1372,"depth":499,"text":1373},[1450,1453,1454],{"name":1451,"item":1452},"Home","\u002F",{"name":24,"item":23},{"name":5,"item":1455},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002F","2026-09-18","Add forms, API proxies, authentication and personalisation to a static site with edge and serverless functions — without giving up static pages or CDN caching.","md",[1460,1461,1462,1463,1464],{"q":1338,"a":1341},{"q":1345,"a":1348},{"q":1352,"a":1355},{"q":1359,"a":1362},{"q":1366,"a":1369},{},true,"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites",{"title":5,"description":1457},"production-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Findex","guide","efzBjD6Vatx7XydOM64aZxpVg5hF2MI6cxMTZUQkfqg",[1473,1476,1479,1482,1485,1488,1491,1494,1497,1500,1503,1506,1509,1512,1515,1518,1521,1524,1527,1530,1533,1536,1539,1542,1545,1548,1551,1554,1557,1560,1563,1566,1569,1572,1575,1578,1581,1584,1587,1590,1593,1596,1599,1602,1605,1608,1611,1614,1617,1620,1623,1626,1629,1632,1635,1638,1641,1644,1647,1650,1653,1656,1659,1662,1665,1668,1671,1674,1677,1680,1683,1686,1689,1692,1695,1698,1701,1704,1707,1710,1713,1716,1719,1722,1725,1728,1731,1734,1737,1740,1743,1746,1749,1752,1755,1758,1761,1764,1767,1770,1773,1776,1779,1782,1785,1788,1791,1794,1797,1800,1803,1806,1809,1812,1815,1818,1821,1823,1826,1829,1832,1835,1838,1841,1843,1846,1849,1852,1855,1858,1861,1864,1867,1870,1873,1876,1879,1882,1885,1888,1891,1894,1897,1899,1902,1905,1908,1910,1912,1915,1918,1920,1923,1926,1929,1932,1935,1938,1941,1944,1947,1950,1953,1956,1959,1962,1965,1968,1971,1974,1977,1980,1983,1986,1989,1992,1995,1998,2000,2002,2003,2005,2007],{"path":1474,"title":1475},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fastro-vs-eleventy-build-times-at-10000-pages","Astro vs Eleventy Build Times at 10,000 Pages",{"path":1477,"title":1478},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1480,"title":1481},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1483,"title":1484},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1486,"title":1487},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fshortcodes-vs-components-for-docs-authors","Shortcodes vs Components for Docs Authors",{"path":1489,"title":1490},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fsidebar-navigation-in-astro-and-eleventy","Sidebar Navigation in Astro and Eleventy",{"path":1492,"title":1493},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fcustomizing-starlight-without-forking-the-theme","Customizing Starlight Without Forking the Theme",{"path":1495,"title":1496},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fdocusaurus-vs-starlight-for-product-documentation","Docusaurus vs Starlight for Product Documentation",{"path":1498,"title":1499},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress","Docs Frameworks: Docusaurus, Starlight and VitePress",{"path":1501,"title":1502},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmdx-vs-markdoc-for-docs-content","MDX vs Markdoc for Docs Content",{"path":1504,"title":1505},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight","Migrating from MkDocs to Starlight",{"path":1507,"title":1508},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fversioned-documentation-with-docusaurus","Versioned Documentation with Docusaurus",{"path":1510,"title":1511},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fvitepress-for-library-documentation","VitePress for Library Documentation",{"path":1513,"title":1514},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1516,"title":1517},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhugo-partialcached-for-faster-builds","Hugo partialCached for Faster Builds",{"path":1519,"title":1520},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1522,"title":1523},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1525,"title":1526},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Freducing-hugo-memory-usage-on-ci-runners","Reducing Hugo Memory Usage on CI Runners",{"path":1528,"title":1529},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1531,"title":1532},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1534,"title":1535},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs","Eleventy vs Jekyll for Markdown-Heavy Blogs",{"path":1537,"title":1538},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1540,"title":1541},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy","Replacing Jekyll Plugins When Migrating to Eleventy",{"path":1543,"title":1544},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins","Running Jekyll on GitHub Pages Without Plugins",{"path":1546,"title":1547},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fspeeding-up-slow-jekyll-builds","Speeding Up Slow Jekyll Builds",{"path":1549,"title":1550},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fupgrading-jekyll-and-ruby-versions-safely","Upgrading Jekyll and Ruby Versions Safely",{"path":1552,"title":1553},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fconverting-front-matter-at-scale-during-migration","Converting Front Matter at Scale During Migration",{"path":1555,"title":1556},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1558,"title":1559},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration","Keeping Redirects Working After an SSG Migration",{"path":1561,"title":1562},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo","Migrating a Docs Site From Jekyll to Hugo",{"path":1564,"title":1565},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-gatsby-to-astro","Migrating from Gatsby to Astro",{"path":1567,"title":1568},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1570,"title":1571},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-wordpress-to-a-static-site-generator","Migrating WordPress to a Static Site Generator",{"path":1573,"title":1574},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators","Porting Shortcodes and Includes Between Generators",{"path":1576,"title":1577},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1579,"title":1580},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1582,"title":1583},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1585,"title":1586},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-app-router-static-export-limitations","Next.js App Router Static Export Limitations",{"path":1588,"title":1589},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1591,"title":1592},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Foptimizing-images-in-nextjs-static-export","Optimizing Images in Next.js Static Export",{"path":1594,"title":1595},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fadding-pagefind-to-an-astro-site","Adding Pagefind to an Astro Site",{"path":1597,"title":1598},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fbuilding-a-lunr-index-at-build-time-in-eleventy","Building a Lunr Index at Build Time in Eleventy",{"path":1600,"title":1601},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites","Search for Static Sites",{"path":1603,"title":1604},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Findexing-hugo-sites-with-pagefind","Indexing Hugo Sites with Pagefind",{"path":1606,"title":1607},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fmultilingual-search-on-static-sites","Multilingual Search on Static Sites",{"path":1609,"title":1610},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fpagefind-vs-algolia-docsearch","Pagefind vs Algolia DocSearch",{"path":1612,"title":1613},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fsearch-index-size-budgets-for-large-docs","Search Index Size Budgets for Large Docs",{"path":1615,"title":1616},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1618,"title":1619},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1621,"title":1622},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fevaluating-ssg-accessibility-defaults","Evaluating SSG Accessibility Defaults",{"path":1624,"title":1625},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1627,"title":1628},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1630,"title":1631},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1633,"title":1634},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Ftotal-cost-of-ownership-for-static-site-generators","Total Cost of Ownership for Static Site Generators",{"path":1636,"title":1637},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fcache-busting-with-content-hashed-filenames","Cache Busting with Content-Hashed Filenames",{"path":1639,"title":1640},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1642,"title":1643},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1645,"title":1646},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1648,"title":1649},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1651,"title":1652},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fstale-while-revalidate-for-static-html","Stale-While-Revalidate for Static HTML",{"path":1654,"title":1655},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts","Eliminating Layout Shift From Web Fonts",{"path":1657,"title":1658},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-cookie-banners","Fixing CLS from Cookie Banners",{"path":1660,"title":1661},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1663,"title":1664},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-sticky-headers-and-anchor-links","Fixing CLS from Sticky Headers and Anchor Links",{"path":1666,"title":1667},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1669,"title":1670},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js","Measuring CLS in the Field With web-vitals.js",{"path":1672,"title":1673},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift","Reserving Space for Images and Embeds",{"path":1675,"title":1676},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Ffont-display-optional-vs-swap","font-display: optional vs swap",{"path":1678,"title":1679},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1681,"title":1682},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fmetric-matched-fallback-fonts-with-size-adjust","Metric-Matched Fallback Fonts with size-adjust",{"path":1684,"title":1685},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fpreloading-fonts-without-double-downloads","Preloading Fonts Without Double Downloads",{"path":1687,"title":1688},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":1690,"title":1691},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render","Subsetting Variable Fonts for Faster First Render",{"path":1693,"title":1694},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":1696,"title":1697},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fgenerating-open-graph-images-at-build-time","Generating Open Graph Images at Build Time",{"path":1699,"title":1700},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1702,"title":1703},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Flazy-loading-images-without-hurting-lcp","Lazy-Loading Images Without Hurting LCP",{"path":1705,"title":1706},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":1708,"title":1709},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fresponsive-images-with-srcset-in-eleventy","Responsive Images with srcset in Eleventy",{"path":1711,"title":1712},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":1714,"title":1715},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1717,"title":1718},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1720,"title":1721},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fdeferring-hydration-with-client-visible-in-astro","Deferring Hydration with client:visible in Astro",{"path":1723,"title":1724},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":1726,"title":1727},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1729,"title":1730},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":1732,"title":1733},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Freplacing-react-islands-with-web-components","Replacing React Islands with Web Components",{"path":1735,"title":1736},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":1738,"title":1739},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Ffixing-lcp-on-text-heavy-documentation-pages","Fixing LCP on Text-Heavy Documentation Pages",{"path":1741,"title":1742},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1744,"title":1745},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Fmeasuring-lcp-subparts-with-devtools","Measuring LCP Subparts with DevTools",{"path":1747,"title":1748},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":1750,"title":1751},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":1753,"title":1754},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Fcomparing-lab-and-field-data-with-crux","Comparing Lab and Field Data with CrUX",{"path":1756,"title":1757},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci","Performance Budgets and Lighthouse CI",{"path":1759,"title":1760},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Freducing-lighthouse-score-variance-in-ci","Reducing Lighthouse Score Variance in CI",{"path":1762,"title":1763},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Frunning-webpagetest-scripts-against-preview-deploys","Running WebPageTest Scripts Against Preview Deploys",{"path":1765,"title":1766},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Fsetting-up-lighthouse-ci-for-a-static-site","Setting Up Lighthouse CI for a Static Site",{"path":1768,"title":1769},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Ftracking-bundle-size-per-pull-request","Tracking Bundle Size per Pull Request",{"path":1771,"title":1772},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Fwriting-a-performance-budget-that-fails-builds","Writing a Performance Budget That Fails Builds",{"path":1774,"title":1775},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fauditing-unused-preloads","Auditing Unused Preloads",{"path":1777,"title":1778},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed","Resource Hints and Navigation Speed",{"path":1780,"title":1781},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Finstant-navigation-with-speculation-rules","Instant Navigation with Speculation Rules",{"path":1783,"title":1784},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fpreconnect-vs-dns-prefetch-on-static-sites","Preconnect vs DNS-Prefetch on Static Sites",{"path":1786,"title":1787},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fprefetching-links-in-astro","Prefetching Links in Astro",{"path":1789,"title":1790},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fview-transitions-on-multi-page-static-sites","View Transitions on Multi-Page Static Sites",{"path":1792,"title":1793},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":1795,"title":1796},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1798,"title":1799},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":1801,"title":1802},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Floading-google-tag-manager-without-hurting-inp","Loading Google Tag Manager Without Hurting INP",{"path":1804,"title":1805},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Frunning-third-party-scripts-in-a-web-worker-with-partytown","Running Third-Party Scripts in a Web Worker with Partytown",{"path":1807,"title":1808},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":1810,"title":1811},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1813,"title":1814},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages","Configuring Redirects on Cloudflare Pages",{"path":1816,"title":1817},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fcustom-domains-and-tls-on-cloudflare-pages","Custom Domains and TLS on Cloudflare Pages",{"path":1819,"title":1820},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":1822,"title":34},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup",{"path":1824,"title":1825},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fmigrating-from-cloudflare-pages-to-workers-static-assets","Migrating from Cloudflare Pages to Workers Static Assets",{"path":1827,"title":1828},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests","Checking Links in Pull Requests",{"path":1830,"title":1831},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":1833,"title":1834},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci","Editorial Checks with Vale in CI",{"path":1836,"title":1837},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1839,"title":1840},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds","Scheduling Content Publication With Cron-Triggered Builds",{"path":1842,"title":966},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build",{"path":1844,"title":1845},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fbuilding-astro-sites-with-github-actions","Building Astro Sites with GitHub Actions",{"path":1847,"title":1848},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":1850,"title":1851},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-github-pages-with-actions","Deploying to GitHub Pages with Actions",{"path":1853,"title":1854},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":1856,"title":1857},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":1859,"title":1860},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1862,"title":1863},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fmatrix-builds-for-multi-site-monorepos","Matrix Builds for Multi-Site Monorepos",{"path":1865,"title":1866},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":1868,"title":1869},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1871,"title":1872},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fincremental-builds-in-astro-with-the-content-layer","Incremental Builds in Astro with the Content Layer",{"path":1874,"title":1875},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1877,"title":1878},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":1880,"title":1881},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fremote-caching-with-turborepo-for-ssg-monorepos","Remote Caching with Turborepo for SSG Monorepos",{"path":1883,"title":1884},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":1886,"title":1887},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1889,"title":1890},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops","Alerting on Cache Hit Ratio Drops",{"path":1892,"title":1893},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fbuilding-a-core-web-vitals-dashboard-from-rum-data","Building a Core Web Vitals Dashboard from RUM Data",{"path":1895,"title":1896},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fcrawling-for-broken-links-on-a-schedule","Crawling for Broken Links on a Schedule",{"path":1898,"title":1139},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production",{"path":1900,"title":1901},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge","Logging 404s at the Edge",{"path":1903,"title":1904},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fuptime-and-synthetic-checks-for-static-sites","Uptime and Synthetic Checks for Static Sites",{"path":1906,"title":1907},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fconfiguring-vercel-for-hugo-and-eleventy","Configuring Vercel for Hugo and Eleventy",{"path":1909,"title":29},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies",{"path":1911,"title":962},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates",{"path":1913,"title":1914},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-redirects-and-rewrites-for-static-sites","Netlify Redirects and Rewrites for Static Sites",{"path":1916,"title":1917},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":1919,"title":1415},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs",{"path":1921,"title":1922},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":1924,"title":1925},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1927,"title":1928},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1930,"title":1931},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments","Password-Protecting Preview Deployments",{"path":1933,"title":1934},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts","Previewing Headless CMS Drafts",{"path":1936,"title":1937},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys","Visual Regression Testing on Preview Deploys",{"path":1939,"title":1940},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1942,"title":1943},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fcanary-releases-for-static-sites","Canary Releases for Static Sites",{"path":1945,"title":1946},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Ffeature-flags-on-static-sites","Feature Flags on Static Sites",{"path":1948,"title":1949},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1951,"title":1952},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":1954,"title":1955},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",{"path":1957,"title":1958},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fauditing-npm-dependencies-in-ssg-pipelines","Auditing npm Dependencies in SSG Pipelines",{"path":1960,"title":1961},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fenabling-hsts-and-preload-safely","Enabling HSTS and Preload Safely",{"path":1963,"title":1964},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fhash-based-csp-for-inline-scripts-in-astro","Hash-Based CSP for Inline Scripts in Astro",{"path":1966,"title":1967},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites","Security Headers and Hardening for Static Sites",{"path":1969,"title":1970},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc","Securing Deploy Credentials with GitHub OIDC",{"path":1972,"title":1973},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsubresource-integrity-for-third-party-assets","Subresource Integrity for Third-Party Assets",{"path":1975,"title":1976},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fwriting-a-content-security-policy-for-a-static-site","Writing a Content Security Policy for a Static Site",{"path":1978,"title":1979},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3","Clean URLs and Trailing Slashes on S3",{"path":1981,"title":1982},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects","CloudFront Functions for Redirects",{"path":1984,"title":1985},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fdeploying-a-static-site-to-s3-and-cloudfront","Deploying a Static Site to S3 and CloudFront",{"path":1987,"title":1988},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites","Self-Hosting Static Sites on S3, Nginx and Caddy",{"path":1990,"title":1991},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy","Serving a Static Site with Caddy",{"path":1993,"title":1994},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx","Serving a Static Site with Nginx",{"path":1996,"title":1997},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps","Zero-Downtime Deploys with Symlink Swaps",{"path":1999,"title":471},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fadding-a-contact-form-with-cloudflare-workers",{"path":2001,"title":466},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fhandling-form-submissions-on-a-static-site",{"path":1467,"title":5},{"path":2004,"title":332},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers",{"path":2006,"title":805},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication",{"path":2008,"title":794},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fproxying-third-party-apis-from-an-edge-function",1789722846729]