[{"data":1,"prerenderedAt":1981},["ShallowReactive",2],{"page:\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites":3,"all-docs-nav":1449},{"id":4,"title":5,"body":6,"breadcrumb":1426,"dateModified":1433,"datePublished":1433,"description":1434,"extension":1435,"faq":1436,"meta":1442,"navigation":596,"path":1443,"seo":1444,"slug":1445,"stem":1446,"type":1447,"__hash__":1448},"content\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Findex.md","Self-Hosting Static Sites on S3, Nginx and Caddy",{"type":7,"value":8,"toc":1401},"minimark",[9,13,17,44,191,196,199,409,423,427,430,541,553,557,560,651,673,676,679,686,693,774,778,781,787,811,815,827,925,929,932,938,944,950,957,961,977,981,984,1044,1047,1051,1066,1070,1100,1104,1107,1143,1147,1150,1229,1232,1236,1284,1288,1305,1309,1314,1317,1321,1324,1328,1331,1335,1338,1342,1345,1349,1397],[10,11,5],"h1",{"id":12},"self-hosting-static-sites-on-s3-nginx-and-caddy",[14,15,16],"p",{},"Managed static hosts — Cloudflare Pages, Netlify, Vercel, GitHub Pages — make publishing a folder of HTML almost effortless, and for most sites they are the right answer. But plenty of teams host static sites themselves: a regulated organisation that must keep data in its own cloud account, a company with a large existing AWS contract, a documentation portal behind corporate single sign-on, a site at a traffic level where per-request pricing matters, or simply a team that wants every header, log line and routing rule under its own control.",[14,18,19,20,24,25,28,29,32,33,38,39,43],{},"Self-hosting a static site is not hard, but it means rebuilding the conveniences a managed host provides: clean URLs, redirects, headers, TLS, cache rules, atomic deploys and rollback. This topic covers the three most common setups — ",[21,22,23],"strong",{},"S3 with CloudFront",", ",[21,26,27],{},"nginx on a VM"," and ",[21,30,31],{},"Caddy on a VM"," — and how to rebuild each convenience. It sits inside ",[34,35,37],"a",{"href":36},"\u002Fproduction-ready-deployment-cicd-workflows\u002F","Production-Ready Deployment & CI\u002FCD Workflows",", next to the managed-host comparison in ",[34,40,42],{"href":41},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002F","Netlify vs Vercel Deployment Strategies",".",[45,46,47,187],"figure",{},[48,49,56,57,56,61,56,65,56,72],"svg",{"viewBox":50,"role":51,"ariaLabelledBy":52,"xmlns":55},"0 0 780 320","img",[53,54],"shs2-arch-title","shs2-arch-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[58,59,60],"title",{"id":53},"Three self-hosting architectures",[62,63,64],"desc",{"id":54},"Three stacks. S3 plus CloudFront: CI uploads to a private S3 bucket, CloudFront serves it globally with a CloudFront Function for URL rewriting and redirects. Nginx on a VM: CI uploads a release directory over SSH, a symlink points to the current release, nginx serves it, optionally behind a CDN. Caddy on a VM: the same release and symlink layout, with Caddy handling TLS certificates automatically.",[66,67],"rect",{"x":68,"y":68,"width":69,"height":70,"fill":71},"0","780","320","#ffffff",[73,74,76,77,76,85,76,96,76,102,76,111,76,116,76,119,76,123,76,126,76,130,76,136,76,141,76,143,76,146,76,149,76,151,76,154,76,156,76,159,76,162,76,167,76,170,76,173,76,175,76,177,76,179,76,181,76,184,56],"g",{"style":75},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[78,79,84],"text",{"x":80,"y":81,"fill":82,"style":83},"390","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Same static files, three ways to serve them",[66,86],{"x":87,"y":88,"width":89,"height":90,"rx":91,"fill":92,"opacity":93,"stroke":94,"style":95},"30","50","230","250","12","#ffca3a","0.1","#a97b00","stroke-width:1.5px",[78,97,101],{"x":98,"y":99,"fill":82,"style":100},"145","76","font-size:14px;font-weight:700;text-anchor:middle","S3 + CloudFront",[66,103],{"x":88,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},"94","190","40","8","#f8fafc","#d9e2ef","stroke-width:1px",[78,112,115],{"x":98,"y":113,"fill":82,"style":114},"119","text-anchor:middle","CI: aws s3 sync",[66,117],{"x":88,"y":118,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},"146",[78,120,122],{"x":98,"y":121,"fill":82,"style":114},"171","private bucket (OAC)",[66,124],{"x":88,"y":125,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},"198",[78,127,129],{"x":98,"y":128,"fill":82,"style":114},"223","CloudFront + Function",[78,131,135],{"x":98,"y":132,"fill":133,"style":134},"270","#556071","font-size:11px;text-anchor:middle","global edge, pay per request",[66,137],{"x":138,"y":88,"width":89,"height":90,"rx":91,"fill":139,"opacity":140,"stroke":139,"style":95},"275","#1982c4","0.08",[78,142,27],{"x":80,"y":99,"fill":82,"style":100},[66,144],{"x":145,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},"295",[78,147,148],{"x":80,"y":113,"fill":82,"style":114},"CI: rsync release dir",[66,150],{"x":145,"y":118,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},[78,152,153],{"x":80,"y":121,"fill":82,"style":114},"current → releases\u002FN",[66,155],{"x":145,"y":125,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},[78,157,158],{"x":80,"y":128,"fill":82,"style":114},"nginx (+ certbot)",[78,160,161],{"x":80,"y":132,"fill":133,"style":134},"full control, fixed cost",[66,163],{"x":164,"y":88,"width":89,"height":90,"rx":91,"fill":165,"opacity":93,"stroke":166,"style":95},"520","#8ac926","#5a8a16",[78,168,31],{"x":169,"y":99,"fill":82,"style":100},"635",[66,171],{"x":172,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},"540",[78,174,148],{"x":169,"y":113,"fill":82,"style":114},[66,176],{"x":172,"y":118,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},[78,178,153],{"x":169,"y":121,"fill":82,"style":114},[66,180],{"x":172,"y":125,"width":105,"height":106,"rx":107,"fill":108,"stroke":109,"style":110},[78,182,183],{"x":169,"y":128,"fill":82,"style":114},"Caddy (automatic TLS)",[78,185,186],{"x":169,"y":132,"fill":133,"style":134},"least config, fixed cost",[188,189,190],"figcaption",{},"All three serve the same build output; they differ in how they handle URLs, TLS, caching and deploys.",[192,193,195],"h2",{"id":194},"what-a-managed-host-was-doing-for-you","What a Managed Host Was Doing for You",[14,197,198],{},"Before choosing a stack, list the features you will need to rebuild. Every managed static host provides roughly the same set:",[200,201,202,223],"table",{},[203,204,205],"thead",{},[206,207,208,212,215,217,220],"tr",{},[209,210,211],"th",{},"Feature",[209,213,214],{},"Managed host",[209,216,101],{},[209,218,219],{},"nginx",[209,221,222],{},"Caddy",[224,225,226,243,275,300,323,347,363,379,395],"tbody",{},[206,227,228,232,235,238,241],{},[229,230,231],"td",{},"TLS certificates",[229,233,234],{},"automatic",[229,236,237],{},"ACM, automatic",[229,239,240],{},"certbot, cron",[229,242,234],{},[206,244,245,257,259,262,267],{},[229,246,247,248,252,253,256],{},"Clean URLs (",[249,250,251],"code",{},"\u002Fguide\u002F"," → ",[249,254,255],{},"\u002Fguide\u002Findex.html",")",[229,258,234],{},[229,260,261],{},"CloudFront Function",[229,263,264],{},[249,265,266],{},"try_files",[229,268,269,271,272],{},[249,270,266],{}," \u002F ",[249,273,274],{},"file_server",[206,276,277,280,286,289,295],{},[229,278,279],{},"Redirect rules",[229,281,282,285],{},[249,283,284],{},"_redirects"," file",[229,287,288],{},"CloudFront Function or S3 rules",[229,290,291,294],{},[249,292,293],{},"return 301"," \u002F map",[229,296,297],{},[249,298,299],{},"redir",[206,301,302,305,310,313,318],{},[229,303,304],{},"Custom headers",[229,306,307,285],{},[249,308,309],{},"_headers",[229,311,312],{},"response headers policy",[229,314,315],{},[249,316,317],{},"add_header",[229,319,320],{},[249,321,322],{},"header",[206,324,325,328,330,333,342],{},[229,326,327],{},"Compression",[229,329,234],{},[229,331,332],{},"CloudFront setting",[229,334,335,24,338,341],{},[249,336,337],{},"gzip",[249,339,340],{},"brotli"," module",[229,343,344],{},[249,345,346],{},"encode zstd gzip",[206,348,349,352,355,358,361],{},[229,350,351],{},"Atomic deploy",[229,353,354],{},"built in",[229,356,357],{},"upload order or origin path switch",[229,359,360],{},"symlink swap",[229,362,360],{},[206,364,365,368,371,374,377],{},[229,366,367],{},"Instant rollback",[229,369,370],{},"one click",[229,372,373],{},"re-point or re-sync",[229,375,376],{},"symlink back",[229,378,376],{},[206,380,381,384,387,390,393],{},[229,382,383],{},"Preview deploys",[229,385,386],{},"per PR",[229,388,389],{},"prefix per PR",[229,391,392],{},"subdomain per PR",[229,394,392],{},[206,396,397,400,402,404,407],{},[229,398,399],{},"Global edge",[229,401,354],{},[229,403,354],{},[229,405,406],{},"add a CDN",[229,408,406],{},[14,410,411,412,414,415,417,418,422],{},"The clean-URL row trips up more S3 deployments than any other. S3's REST endpoint does not resolve ",[249,413,251],{}," to ",[249,416,255],{},"; without a rewrite, every directory-style URL returns an XML error. ",[34,419,421],{"href":420},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3\u002F","Clean URLs and Trailing Slashes on S3"," covers the fix.",[192,424,426],{"id":425},"s3-and-cloudfront","S3 and CloudFront",[14,428,429],{},"For AWS-centric organisations this is the default. The bucket stays private; CloudFront reads it through Origin Access Control, serves from over 400 edge locations, terminates TLS with a free ACM certificate, and runs small CloudFront Functions on each request for URL rewriting and redirects.",[431,432,437],"pre",{"className":433,"code":434,"language":435,"meta":436,"style":436},"language-bash shiki shiki-themes github-light github-dark","aws s3 sync dist\u002F s3:\u002F\u002Fdocs-example-com\u002F --delete \\\n  --exclude '*.html' --cache-control 'public, max-age=31536000, immutable'\naws s3 sync dist\u002F s3:\u002F\u002Fdocs-example-com\u002F --delete \\\n  --exclude '*' --include '*.html' --cache-control 'public, max-age=0, must-revalidate'\naws cloudfront create-invalidation --distribution-id E2ABC123 --paths '\u002F*'\n","bash","",[249,438,439,468,483,500,518],{"__ignoreMap":436},[440,441,444,448,452,455,458,461,465],"span",{"class":442,"line":443},"line",1,[440,445,447],{"class":446},"sScJk","aws",[440,449,451],{"class":450},"sZZnC"," s3",[440,453,454],{"class":450}," sync",[440,456,457],{"class":450}," dist\u002F",[440,459,460],{"class":450}," s3:\u002F\u002Fdocs-example-com\u002F",[440,462,464],{"class":463},"sj4cs"," --delete",[440,466,467],{"class":463}," \\\n",[440,469,471,474,477,480],{"class":442,"line":470},2,[440,472,473],{"class":463},"  --exclude",[440,475,476],{"class":450}," '*.html'",[440,478,479],{"class":463}," --cache-control",[440,481,482],{"class":450}," 'public, max-age=31536000, immutable'\n",[440,484,486,488,490,492,494,496,498],{"class":442,"line":485},3,[440,487,447],{"class":446},[440,489,451],{"class":450},[440,491,454],{"class":450},[440,493,457],{"class":450},[440,495,460],{"class":450},[440,497,464],{"class":463},[440,499,467],{"class":463},[440,501,503,505,508,511,513,515],{"class":442,"line":502},4,[440,504,473],{"class":463},[440,506,507],{"class":450}," '*'",[440,509,510],{"class":463}," --include",[440,512,476],{"class":450},[440,514,479],{"class":463},[440,516,517],{"class":450}," 'public, max-age=0, must-revalidate'\n",[440,519,521,523,526,529,532,535,538],{"class":442,"line":520},5,[440,522,447],{"class":446},[440,524,525],{"class":450}," cloudfront",[440,527,528],{"class":450}," create-invalidation",[440,530,531],{"class":463}," --distribution-id",[440,533,534],{"class":450}," E2ABC123",[440,536,537],{"class":463}," --paths",[440,539,540],{"class":450}," '\u002F*'\n",[14,542,543,544,548,549,43],{},"Two uploads with different cache headers, assets first and HTML last, keep pages consistent during the deploy: new HTML never references an asset that has not been uploaded yet. The full setup is in ",[34,545,547],{"href":546},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fdeploying-a-static-site-to-s3-and-cloudfront\u002F","Deploying a Static Site to S3 and CloudFront",", and redirect handling in ",[34,550,552],{"href":551},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects\u002F","CloudFront Functions for Redirects",[192,554,556],{"id":555},"nginx-on-a-vm","Nginx on a VM",[14,558,559],{},"Nginx is fast, universally understood and gives complete control. A static site needs very little of it:",[431,561,564],{"className":562,"code":563,"language":219,"meta":436,"style":436},"language-nginx shiki shiki-themes github-light github-dark","server {\n  listen 443 ssl http2;\n  server_name docs.example.com;\n  root \u002Fsrv\u002Fdocs\u002Fcurrent;\n  include \u002Fetc\u002Fnginx\u002Fsnippets\u002Fsecurity-headers.conf;\n\n  location \u002F {\n    try_files $uri $uri\u002Findex.html $uri.html =404;\n  }\n  location \u002F_astro\u002F {\n    include \u002Fetc\u002Fnginx\u002Fsnippets\u002Fsecurity-headers.conf;\n    add_header Cache-Control \"public, max-age=31536000, immutable\";\n  }\n  error_page 404 \u002F404.html;\n}\n",[249,565,566,571,576,581,586,591,598,604,610,616,622,628,634,639,645],{"__ignoreMap":436},[440,567,568],{"class":442,"line":443},[440,569,570],{},"server {\n",[440,572,573],{"class":442,"line":470},[440,574,575],{},"  listen 443 ssl http2;\n",[440,577,578],{"class":442,"line":485},[440,579,580],{},"  server_name docs.example.com;\n",[440,582,583],{"class":442,"line":502},[440,584,585],{},"  root \u002Fsrv\u002Fdocs\u002Fcurrent;\n",[440,587,588],{"class":442,"line":520},[440,589,590],{},"  include \u002Fetc\u002Fnginx\u002Fsnippets\u002Fsecurity-headers.conf;\n",[440,592,594],{"class":442,"line":593},6,[440,595,597],{"emptyLinePlaceholder":596},true,"\n",[440,599,601],{"class":442,"line":600},7,[440,602,603],{},"  location \u002F {\n",[440,605,607],{"class":442,"line":606},8,[440,608,609],{},"    try_files $uri $uri\u002Findex.html $uri.html =404;\n",[440,611,613],{"class":442,"line":612},9,[440,614,615],{},"  }\n",[440,617,619],{"class":442,"line":618},10,[440,620,621],{},"  location \u002F_astro\u002F {\n",[440,623,625],{"class":442,"line":624},11,[440,626,627],{},"    include \u002Fetc\u002Fnginx\u002Fsnippets\u002Fsecurity-headers.conf;\n",[440,629,631],{"class":442,"line":630},12,[440,632,633],{},"    add_header Cache-Control \"public, max-age=31536000, immutable\";\n",[440,635,637],{"class":442,"line":636},13,[440,638,615],{},[440,640,642],{"class":442,"line":641},14,[440,643,644],{},"  error_page 404 \u002F404.html;\n",[440,646,648],{"class":442,"line":647},15,[440,649,650],{},"}\n",[14,652,653,654,657,658,661,662,664,665,668,669,43],{},"The ",[249,655,656],{},"include"," inside the ",[249,659,660],{},"location"," block is not redundant: nginx's ",[249,663,317],{}," directives in a nested block replace those from the parent rather than adding to them, so a location that sets ",[249,666,667],{},"Cache-Control"," loses every security header unless they are repeated. It is the most common self-hosting mistake, and it is invisible until someone checks the headers on an asset. The complete configuration is in ",[34,670,672],{"href":671},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx\u002F","Serving a Static Site with Nginx",[192,674,31],{"id":675},"caddy-on-a-vm",[14,677,678],{},"Caddy does less configuration for the same result. It obtains and renews TLS certificates automatically, redirects HTTP to HTTPS, and enables modern protocols by default:",[431,680,684],{"className":681,"code":683,"language":78,"meta":436},[682],"language-text","docs.example.com {\n  root * \u002Fsrv\u002Fdocs\u002Fcurrent\n  encode zstd gzip\n  try_files {path} {path}\u002Findex.html {path}.html\n  file_server\n  header \u002F_astro\u002F* Cache-Control \"public, max-age=31536000, immutable\"\n  import security_headers\n  handle_errors {\n    rewrite * \u002F404.html\n    file_server\n  }\n}\n",[249,685,683],{"__ignoreMap":436},[14,687,688,689,43],{},"For a single team running a handful of static sites on one VM, Caddy is the lowest-maintenance option available. See ",[34,690,692],{"href":691},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy\u002F","Serving a Static Site with Caddy",[45,694,695,771],{},[48,696,56,701,56,704,56,707,56,710],{"viewBox":697,"role":51,"ariaLabelledBy":698,"xmlns":55},"0 0 780 290",[699,700],"shs2-cfg-title","shs2-cfg-desc",[58,702,703],{"id":699},"Configuration effort for the same feature set",[62,705,706],{"id":700},"Bars of lines of configuration needed for TLS, clean URLs, redirects, headers, compression and caching. Caddy needed about 20 lines. Nginx needed about 70 lines plus a certbot timer. S3 plus CloudFront needed about 110 lines of Terraform plus a 40-line CloudFront Function.",[66,708],{"x":68,"y":68,"width":69,"height":709,"fill":71},"290",[73,711,76,712,76,715,76,720,76,724,76,730,76,734,76,737,76,742,76,746,76,749,76,753,76,758,76,763,76,767,56],{"style":75},[78,713,714],{"x":80,"y":81,"fill":82,"style":83},"Lines of configuration for the same features",[442,716],{"x1":717,"y1":718,"x2":717,"y2":719,"stroke":109,"style":95},"220","46","236",[78,721,222],{"x":106,"y":722,"fill":82,"style":723},"84","font-weight:700",[66,725],{"x":717,"y":726,"width":727,"height":81,"rx":728,"fill":165,"opacity":729,"stroke":166,"style":110},"66","60","4","0.35",[78,731,733],{"x":709,"y":732,"fill":82},"85","~20 lines",[78,735,219],{"x":106,"y":736,"fill":82,"style":723},"144",[66,738],{"x":717,"y":739,"width":740,"height":81,"rx":728,"fill":139,"opacity":741,"stroke":139,"style":110},"126","210","0.25",[78,743,745],{"x":744,"y":98,"fill":82},"440","~70 lines + certbot timer",[78,747,101],{"x":106,"y":748,"fill":82,"style":723},"204",[66,750],{"x":717,"y":751,"width":752,"height":81,"rx":728,"fill":92,"opacity":729,"stroke":94,"style":110},"186","330",[66,754],{"x":755,"y":751,"width":756,"height":81,"rx":728,"fill":92,"opacity":757,"stroke":94,"style":110},"550","120","0.2",[78,759,762],{"x":760,"y":761,"fill":82,"style":114},"385","205","~110 lines Terraform",[78,764,766],{"x":765,"y":761,"fill":82,"style":134},"610","+ 40-line Function",[78,768,770],{"x":80,"y":769,"fill":133,"style":134},"266","TLS, clean URLs, 25 redirects, security headers, compression, two cache tiers",[188,772,773],{},"More configuration buys more scale: S3 and CloudFront take the most setup and need the least operating afterwards at high traffic.",[192,775,777],{"id":776},"atomic-deploys-and-rollback-without-a-managed-host","Atomic Deploys and Rollback Without a Managed Host",[14,779,780],{},"A managed host swaps the whole site at once and keeps previous deploys for instant rollback. On a VM, the equivalent is a release directory per deploy and a symlink that points to the current one:",[431,782,785],{"className":783,"code":784,"language":78,"meta":436},[682],"\u002Fsrv\u002Fdocs\u002F\n├── releases\u002F\n│   ├── 20260915-1412-a1b2c3d\u002F\n│   ├── 20260917-0931-e4f5a6b\u002F\n│   └── 20260918-1106-c7d8e9f\u002F\n└── current -> releases\u002F20260918-1106-c7d8e9f\n",[249,786,784],{"__ignoreMap":436},[14,788,789,790,793,794,797,798,801,802,806,807,810],{},"CI uploads a new release directory with ",[249,791,792],{},"rsync",", then replaces the symlink with ",[249,795,796],{},"ln -sfn"," followed by an atomic ",[249,799,800],{},"mv -T",". Every request sees either the whole old site or the whole new one. Rolling back is re-pointing the symlink at the previous directory — under a second. ",[34,803,805],{"href":804},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps\u002F","Zero-Downtime Deploys with Symlink Swaps"," has the script and the retention policy. On S3, the equivalent is uploading to a versioned prefix (",[249,808,809],{},"\u002Freleases\u002Fc7d8e9f\u002F",") and changing the CloudFront origin path, or relying on upload ordering as shown above.",[192,812,814],{"id":813},"caching-in-front-of-a-vm","Caching in Front of a VM",[14,816,817,818,821,822,826],{},"A single VM in one region serves readers on other continents slowly: from a Frankfurt VM, time to first byte measured 38 ms from Berlin, 160 ms from New York and 310 ms from Sydney. A CDN in front removes most of that. Cloudflare's free plan or CloudFront with the VM as a custom origin both work. The cache policy is the same one used for managed hosts — long ",[249,819,820],{},"immutable"," lifetimes for hashed assets, short or revalidating lifetimes for HTML — described in ",[34,823,825],{"href":824},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002F","CDN Caching Rules for SSGs",". With the CDN in place, the VM mostly serves cache revalidations and can be very small.",[45,828,829,922],{},[48,830,56,835,56,838,56,841,56,843],{"viewBox":831,"role":51,"ariaLabelledBy":832,"xmlns":55},"0 0 780 270",[833,834],"shs2-ttfb-title","shs2-ttfb-desc",[58,836,837],{"id":833},"Time to first byte by reader location",[62,839,840],{"id":834},"Paired bars for three cities. From Berlin, a Frankfurt VM alone gives 38 milliseconds and with a CDN 22. From New York, 160 alone and 29 with a CDN. From Sydney, 310 alone and 34 with a CDN.",[66,842],{"x":68,"y":68,"width":69,"height":132,"fill":71},[73,844,76,845,76,848,76,852,76,856,76,863,76,868,76,871,76,878,76,882,76,886,76,891,76,894,76,898,76,902,76,906,76,911,76,914,76,918,56],{"style":75},[78,846,847],{"x":80,"y":81,"fill":82,"style":83},"TTFB for a cached HTML page, Frankfurt VM (ms)",[442,849],{"x1":850,"y1":718,"x2":850,"y2":851,"stroke":109,"style":95},"170","226",[78,853,855],{"x":106,"y":854,"fill":82,"style":723},"80","Berlin",[66,857],{"x":850,"y":858,"width":859,"height":860,"rx":861,"fill":139,"opacity":862,"stroke":139,"style":110},"62","68","18","3","0.3",[78,864,867],{"x":865,"y":99,"fill":82,"style":866},"246","font-size:11px","38 VM only",[66,869],{"x":850,"y":722,"width":106,"height":860,"rx":861,"fill":165,"opacity":870,"stroke":166,"style":110},"0.4",[78,872,877],{"x":873,"y":874,"fill":875,"style":876},"218","98","#3f6410","font-size:11px;font-weight:700","22 with CDN",[78,879,881],{"x":106,"y":880,"fill":82,"style":723},"140","New York",[66,883],{"x":850,"y":884,"width":885,"height":860,"rx":861,"fill":139,"opacity":862,"stroke":139,"style":110},"122","288",[78,887,890],{"x":888,"y":889,"fill":82,"style":866},"466","136","160 VM only",[66,892],{"x":850,"y":736,"width":893,"height":860,"rx":861,"fill":165,"opacity":870,"stroke":166,"style":110},"52",[78,895,897],{"x":89,"y":896,"fill":875,"style":876},"158","29 with CDN",[78,899,901],{"x":106,"y":900,"fill":82,"style":723},"200","Sydney",[66,903],{"x":850,"y":904,"width":905,"height":860,"rx":861,"fill":139,"opacity":862,"stroke":139,"style":110},"182","558",[78,907,910],{"x":908,"y":909,"fill":82,"style":866},"600","196","310 VM only",[66,912],{"x":850,"y":748,"width":913,"height":860,"rx":861,"fill":165,"opacity":870,"stroke":166,"style":110},"61",[78,915,917],{"x":916,"y":873,"fill":875,"style":876},"239","34 with CDN",[78,919,921],{"x":80,"y":920,"fill":133,"style":134},"254","WebPageTest, median of 5, HTML cached at the edge with stale-while-revalidate",[188,923,924],{},"Distance dominates TTFB without a CDN; with one, every city lands within a few milliseconds of the others.",[192,926,928],{"id":927},"choosing-between-the-three","Choosing Between the Three",[14,930,931],{},"The table of features is the same for all three stacks, so the choice comes down to who operates it and at what scale.",[14,933,934,937],{},[21,935,936],{},"Choose S3 and CloudFront"," when the organisation already runs on AWS, when traffic is high or spiky, or when compliance requires the content to stay inside a specific cloud account with IAM-controlled access and CloudTrail logging. There is no server to patch, capacity is effectively unlimited, and everything can be described in Terraform. The costs are setup complexity — URL rewriting and redirects need a CloudFront Function — and slightly slower deploys because of cache invalidation.",[14,939,940,943],{},[21,941,942],{},"Choose nginx"," when you already operate nginx elsewhere, need fine-grained control over request handling (complex redirect maps, authentication subrequests, logging formats), or are putting the static site next to other services on the same host. Its configuration language is widely known, which matters when the person on call at 2 a.m. is not the person who wrote it.",[14,945,946,949],{},[21,947,948],{},"Choose Caddy"," for the smallest operational footprint on a VM: one binary, automatic HTTPS with renewal, sensible defaults, and a config file a newcomer can read in a minute. It is the natural choice for internal documentation portals, staging mirrors and small teams without dedicated infrastructure staff.",[14,951,952,953,956],{},"Whatever the choice, keep the build output identical across hosts. A site that builds the same ",[249,954,955],{},"dist\u002F"," folder for Cloudflare Pages, S3 and a VM can move between them in an afternoon, and a disaster-recovery plan can be as simple as a second deploy target.",[192,958,960],{"id":959},"security-on-self-hosted-stacks","Security on Self-Hosted Stacks",[14,962,963,964,968,969,972,973,976],{},"A managed host hardens the server layer for you; self-hosting hands that job back. For S3, the essentials are a bucket with Block Public Access enabled, Origin Access Control so only CloudFront can read it, and a deploy role limited to that one bucket and distribution. For a VM, they are key-only SSH, a firewall allowing only ports 22 (ideally restricted by source address), 80 and 443, unattended security updates, and a deploy user that can write to the releases directory and nothing else. On all three stacks, the security header set from ",[34,965,967],{"href":966},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002F","Security Headers and Hardening for Static Sites"," applies unchanged — only the syntax differs. Directory listing must be off (it is by default in nginx and Caddy), and dotfiles such as a stray ",[249,970,971],{},".env"," or ",[249,974,975],{},".git"," folder copied into the build output must be denied explicitly, because the server will otherwise serve anything in the web root.",[192,978,980],{"id":979},"cost-at-different-traffic-levels","Cost at Different Traffic Levels",[14,982,983],{},"Self-hosting is sometimes chosen for cost, and the answer depends heavily on traffic. Monthly estimates for a 2,000-page docs site averaging 350 KB per page view, in USD at list prices:",[200,985,986,1001],{},[203,987,988],{},[206,989,990,993,996,998],{},[209,991,992],{},"Monthly page views",[209,994,995],{},"Managed host (free\u002Fpro tier)",[209,997,101],{},[209,999,1000],{},"VM + CDN (free CDN tier)",[224,1002,1003,1016,1030],{},[206,1004,1005,1008,1010,1013],{},[229,1006,1007],{},"100,000",[229,1009,68],{},[229,1011,1012],{},"~4",[229,1014,1015],{},"~6 (smallest VM)",[206,1017,1018,1021,1024,1027],{},[229,1019,1020],{},"1,000,000",[229,1022,1023],{},"0–20",[229,1025,1026],{},"~35",[229,1028,1029],{},"~6",[206,1031,1032,1035,1038,1041],{},[229,1033,1034],{},"10,000,000",[229,1036,1037],{},"20–plan limits",[229,1039,1040],{},"~310",[229,1042,1043],{},"~12 (larger VM)",[14,1045,1046],{},"The numbers above are estimates that move with provider pricing, but the shape is stable: managed hosts are cheapest at small and medium scale, S3 and CloudFront scale linearly with requests and bytes, and a VM behind a free CDN tier is nearly flat. Engineering time usually outweighs all three.",[192,1048,1050],{"id":1049},"logs-and-observability","Logs and Observability",[14,1052,1053,1054,1056,1057,28,1061,1065],{},"One genuine advantage of self-hosting is complete access to request logs. Managed hosts expose limited analytics; your own stack can keep every request. CloudFront standard logs (or real-time logs to Kinesis) and nginx or Caddy access logs record path, status, bytes, cache status and user agent for every hit. Three reports built from them pay for themselves quickly: 404s by path and referrer, which reveal broken links and missing redirects after a migration; cache hit ratio by path prefix, which catches a mis-set ",[249,1055,667],{}," header before it becomes an origin bill; and top user agents by request volume, which separates aggressive crawlers from readers. Keep logs for thirty to ninety days and ship them somewhere queryable — Athena over S3 for CloudFront, or a lightweight pipeline such as Vector into ClickHouse or Loki for a VM. The reports themselves are described in ",[34,1058,1060],{"href":1059},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge\u002F","Logging 404s at the Edge",[34,1062,1064],{"href":1063},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops\u002F","Alerting on Cache Hit Ratio Drops",". Mind privacy: IP addresses in access logs are personal data in many jurisdictions, so truncate or hash them at ingestion if you do not need them.",[192,1067,1069],{"id":1068},"previews-and-ci","Previews and CI",[14,1071,1072,1073,1076,1077,1080,1081,1084,1085,1089,1090,972,1093,1095,1096,43],{},"Self-hosting does not have to mean giving up preview deploys. On S3, deploy each pull request to a ",[249,1074,1075],{},"pr-123\u002F"," prefix served by a preview distribution; on a VM, deploy to ",[249,1078,1079],{},"\u002Fsrv\u002Fpreviews\u002Fpr-123\u002F"," and serve ",[249,1082,1083],{},"pr-123.preview.example.com"," with a wildcard certificate and a single server block that maps the subdomain to the directory. The GitHub Actions wiring is the same as for managed hosts — see ",[34,1086,1088],{"href":1087},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions\u002F","Automating Preview Deploy Pipelines with GitHub Actions"," — with the upload step swapped for ",[249,1091,1092],{},"aws s3 sync",[249,1094,792],{},". Clean up previews when pull requests close, exactly as described in ",[34,1097,1099],{"href":1098},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments\u002F","Cleaning Up Stale Preview Deployments",[192,1101,1103],{"id":1102},"operating-it","Operating It",[14,1105,1106],{},"Self-hosting adds operational work that managed hosts hide:",[1108,1109,1110,1117,1123,1133],"ul",{},[1111,1112,1113,1116],"li",{},[21,1114,1115],{},"Certificate renewal."," Caddy and ACM renew automatically; certbot needs a timer and an alert if renewal fails.",[1111,1118,1119,1122],{},[21,1120,1121],{},"OS and server updates."," A VM needs patching. Unattended security upgrades plus a monthly maintenance window cover most of it.",[1111,1124,1125,1128,1129,43],{},[21,1126,1127],{},"Monitoring."," Uptime checks, certificate expiry alerts and disk space on the VM (old releases accumulate). The monitoring section applies directly: ",[34,1130,1132],{"href":1131},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fuptime-and-synthetic-checks-for-static-sites\u002F","Uptime and Synthetic Checks for Static Sites",[1111,1134,1135,1138,1139,43],{},[21,1136,1137],{},"Access."," Deploys should use a dedicated user with write access only to the release directory, and credentials issued per run, as in ",[34,1140,1142],{"href":1141},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc\u002F","Securing Deploy Credentials with GitHub OIDC",[192,1144,1146],{"id":1145},"measured-impact","Measured Impact",[14,1148,1149],{},"A 2,000-page documentation site moved from a managed host to S3 and CloudFront for compliance reasons. Performance from Lighthouse and RUM; deploy data from CI.",[200,1151,1152,1163],{},[203,1153,1154],{},[206,1155,1156,1159,1161],{},[209,1157,1158],{},"Measure",[209,1160,214],{},[209,1162,101],{},[224,1164,1165,1176,1186,1197,1208,1219],{},[206,1166,1167,1170,1173],{},[229,1168,1169],{},"TTFB p75 (RUM, global)",[229,1171,1172],{},"92 ms",[229,1174,1175],{},"88 ms",[206,1177,1178,1181,1184],{},[229,1179,1180],{},"LCP p75, mobile",[229,1182,1183],{},"1.7 s",[229,1185,1183],{},[206,1187,1188,1191,1194],{},[229,1189,1190],{},"Deploy time (upload + invalidation)",[229,1192,1193],{},"48 s",[229,1195,1196],{},"71 s",[206,1198,1199,1202,1205],{},[229,1200,1201],{},"Rollback time",[229,1203,1204],{},"~10 s (one click)",[229,1206,1207],{},"~40 s (re-point origin path)",[206,1209,1210,1213,1216],{},[229,1211,1212],{},"Setup effort",[229,1214,1215],{},"~1 hour",[229,1217,1218],{},"~3 days including Terraform and Function",[206,1220,1221,1224,1226],{},[229,1222,1223],{},"Monthly cost (~1.2M page views)",[229,1225,68],{},[229,1227,1228],{},"~42 USD",[14,1230,1231],{},"Readers noticed nothing: edge performance was equivalent. The team paid in setup effort, slightly slower deploys and a monthly bill, and gained data residency, IAM-controlled access and CloudTrail audit logs, which were the reasons for the move.",[192,1233,1235],{"id":1234},"common-pitfalls","Common Pitfalls",[1108,1237,1238,1244,1250,1266,1272,1278],{},[1111,1239,1240,1243],{},[21,1241,1242],{},"Public S3 website endpoints."," They serve over HTTP only and expose the bucket. Use a private bucket behind CloudFront with Origin Access Control.",[1111,1245,1246,1249],{},[21,1247,1248],{},"Forgetting clean URLs."," Directory-style links return errors from S3 until a rewrite is added.",[1111,1251,1252,1258,1259,1262,1263,1265],{},[21,1253,1254,1255,1257],{},"nginx ",[249,1256,317],{}," inheritance."," Headers set in ",[249,1260,1261],{},"server"," vanish in any ",[249,1264,660],{}," that sets its own. Use an include file in each block.",[1111,1267,1268,1271],{},[21,1269,1270],{},"One cache lifetime for everything."," Hashed assets want a year; HTML wants revalidation. A single policy is wrong for one of them.",[1111,1273,1274,1277],{},[21,1275,1276],{},"No release retention."," Symlink deploys fill the disk; keep the last ten releases and delete the rest.",[1111,1279,1280,1283],{},[21,1281,1282],{},"Rebuilding previews last."," Losing preview deploys in a migration slows every review; plan them from the start.",[192,1285,1287],{"id":1286},"key-takeaways","Key Takeaways",[1108,1289,1290,1293,1296,1299,1302],{},[1111,1291,1292],{},"Self-hosting means rebuilding what managed hosts provide: TLS, clean URLs, redirects, headers, atomic deploys, rollback and previews.",[1111,1294,1295],{},"Caddy is the least configuration for a VM; nginx gives the most control; S3 with CloudFront scales without servers.",[1111,1297,1298],{},"Atomic deploys on a VM are a release directory and a symlink swap; rollback is re-pointing the symlink.",[1111,1300,1301],{},"Put a CDN in front of a VM for global readers; edge performance then matches managed hosts.",[1111,1303,1304],{},"Choose self-hosting for control, compliance or scale economics — not to make a small site faster.",[192,1306,1308],{"id":1307},"faq","FAQ",[1310,1311,1313],"h3",{"id":1312},"why-self-host-a-static-site-when-managed-hosts-are-free","Why self-host a static site when managed hosts are free?",[14,1315,1316],{},"Common reasons are compliance or data-residency requirements, an existing cloud account and contract, predictable costs at very high traffic, integration with internal authentication, or needing full control over headers, logs and routing. For most small sites a managed static host remains simpler.",[1310,1318,1320],{"id":1319},"which-self-hosting-option-is-simplest","Which self-hosting option is simplest?",[14,1322,1323],{},"Caddy on a small VM. It serves files, obtains and renews TLS certificates automatically, compresses responses and supports clean URLs with a few lines of configuration. S3 with CloudFront scales further but needs more setup for URLs and redirects.",[1310,1325,1327],{"id":1326},"how-do-i-get-atomic-deploys-without-a-managed-host","How do I get atomic deploys without a managed host?",[14,1329,1330],{},"On a VM, upload each build to a new release directory and switch a symlink to it, which is an atomic filesystem operation. On S3, upload hashed assets first and HTML last, or deploy to a versioned prefix and switch the CloudFront origin path.",[1310,1332,1334],{"id":1333},"do-i-still-need-a-cdn-if-i-self-host-on-a-vm","Do I still need a CDN if I self-host on a VM?",[14,1336,1337],{},"For a global audience, usually yes. A single VM adds latency for distant readers and is a single point of failure. Putting a CDN in front with long cache lifetimes for hashed assets gives most of a managed host's performance.",[1310,1339,1341],{"id":1340},"what-do-managed-hosts-do-that-i-will-have-to-rebuild","What do managed hosts do that I will have to rebuild?",[14,1343,1344],{},"Atomic deploys and instant rollback, preview deploys per pull request, clean URL resolution, redirect rules, header configuration, TLS certificates and edge caching. Each is achievable when self-hosting, but each is work you now own.",[192,1346,1348],{"id":1347},"related","Related",[1108,1350,1351,1360,1365,1370,1375,1380,1385,1390],{},[1111,1352,1353,1356,1357,1359],{},[21,1354,1355],{},"Up:"," ",[34,1358,37],{"href":36}," — deployment options in context.",[1111,1361,1362,1364],{},[34,1363,547],{"href":546}," — the AWS setup end to end.",[1111,1366,1367,1369],{},[34,1368,672],{"href":671}," — a complete server block.",[1111,1371,1372,1374],{},[34,1373,692],{"href":691}," — automatic TLS and minimal config.",[1111,1376,1377,1379],{},[34,1378,421],{"href":420}," — making directory URLs work.",[1111,1381,1382,1384],{},[34,1383,552],{"href":551}," — redirects at the edge.",[1111,1386,1387,1389],{},[34,1388,805],{"href":804}," — atomic releases on a VM.",[1111,1391,1392,1396],{},[34,1393,1395],{"href":1394},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads\u002F","Atomic Deploys vs Incremental Uploads"," — why deploy atomicity matters.",[1398,1399,1400],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#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 .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":436,"searchDepth":470,"depth":470,"links":1402},[1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1425],{"id":194,"depth":470,"text":195},{"id":425,"depth":470,"text":426},{"id":555,"depth":470,"text":556},{"id":675,"depth":470,"text":31},{"id":776,"depth":470,"text":777},{"id":813,"depth":470,"text":814},{"id":927,"depth":470,"text":928},{"id":959,"depth":470,"text":960},{"id":979,"depth":470,"text":980},{"id":1049,"depth":470,"text":1050},{"id":1068,"depth":470,"text":1069},{"id":1102,"depth":470,"text":1103},{"id":1145,"depth":470,"text":1146},{"id":1234,"depth":470,"text":1235},{"id":1286,"depth":470,"text":1287},{"id":1307,"depth":470,"text":1308,"children":1419},[1420,1421,1422,1423,1424],{"id":1312,"depth":485,"text":1313},{"id":1319,"depth":485,"text":1320},{"id":1326,"depth":485,"text":1327},{"id":1333,"depth":485,"text":1334},{"id":1340,"depth":485,"text":1341},{"id":1347,"depth":470,"text":1348},[1427,1430,1431],{"name":1428,"item":1429},"Home","\u002F",{"name":37,"item":36},{"name":5,"item":1432},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002F","2026-09-18","Run a static site on infrastructure you control: S3 with CloudFront, nginx or Caddy on a VM, with clean URLs, caching, redirects and zero-downtime deploys.","md",[1437,1438,1439,1440,1441],{"q":1313,"a":1316},{"q":1320,"a":1323},{"q":1327,"a":1330},{"q":1334,"a":1337},{"q":1341,"a":1344},{},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites",{"title":5,"description":1434},"self-hosting-static-sites","production-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Findex","guide","QJw_7_mDUO_FwRy-bpTsUzkKsUZGs8P95DAEMmr71ew",[1450,1453,1456,1459,1462,1465,1468,1471,1474,1477,1480,1483,1486,1489,1492,1495,1498,1501,1504,1507,1510,1513,1516,1519,1522,1525,1528,1531,1534,1537,1540,1543,1546,1549,1552,1555,1558,1561,1564,1567,1570,1573,1576,1579,1582,1585,1588,1591,1594,1597,1600,1603,1606,1609,1612,1615,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,1824,1827,1830,1833,1836,1839,1842,1845,1848,1851,1854,1857,1860,1863,1866,1868,1871,1874,1877,1879,1881,1884,1886,1889,1892,1895,1898,1900,1902,1905,1908,1911,1914,1916,1919,1922,1925,1928,1931,1934,1937,1940,1942,1944,1947,1950,1952,1954,1956,1957,1959,1961,1963,1966,1969,1972,1975,1978],{"path":1451,"title":1452},"\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":1454,"title":1455},"\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":1457,"title":1458},"\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":1460,"title":1461},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1463,"title":1464},"\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":1466,"title":1467},"\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":1469,"title":1470},"\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":1472,"title":1473},"\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":1475,"title":1476},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress","Docs Frameworks: Docusaurus, Starlight and VitePress",{"path":1478,"title":1479},"\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":1481,"title":1482},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight","Migrating from MkDocs to Starlight",{"path":1484,"title":1485},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fversioned-documentation-with-docusaurus","Versioned Documentation with Docusaurus",{"path":1487,"title":1488},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fvitepress-for-library-documentation","VitePress for Library Documentation",{"path":1490,"title":1491},"\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":1493,"title":1494},"\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":1496,"title":1497},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1499,"title":1500},"\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":1502,"title":1503},"\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":1505,"title":1506},"\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":1508,"title":1509},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1511,"title":1512},"\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":1514,"title":1515},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1517,"title":1518},"\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":1520,"title":1521},"\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":1523,"title":1524},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fspeeding-up-slow-jekyll-builds","Speeding Up Slow Jekyll Builds",{"path":1526,"title":1527},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fupgrading-jekyll-and-ruby-versions-safely","Upgrading Jekyll and Ruby Versions Safely",{"path":1529,"title":1530},"\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":1532,"title":1533},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1535,"title":1536},"\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":1538,"title":1539},"\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":1541,"title":1542},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-gatsby-to-astro","Migrating from Gatsby to Astro",{"path":1544,"title":1545},"\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":1547,"title":1548},"\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":1550,"title":1551},"\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":1553,"title":1554},"\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":1556,"title":1557},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1559,"title":1560},"\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":1562,"title":1563},"\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":1565,"title":1566},"\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":1568,"title":1569},"\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":1571,"title":1572},"\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":1574,"title":1575},"\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":1577,"title":1578},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites","Search for Static Sites",{"path":1580,"title":1581},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Findexing-hugo-sites-with-pagefind","Indexing Hugo Sites with Pagefind",{"path":1583,"title":1584},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fmultilingual-search-on-static-sites","Multilingual Search on Static Sites",{"path":1586,"title":1587},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fpagefind-vs-algolia-docsearch","Pagefind vs Algolia DocSearch",{"path":1589,"title":1590},"\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":1592,"title":1593},"\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":1595,"title":1596},"\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":1598,"title":1599},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fevaluating-ssg-accessibility-defaults","Evaluating SSG Accessibility Defaults",{"path":1601,"title":1602},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1604,"title":1605},"\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":1607,"title":1608},"\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":1610,"title":1611},"\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":1613,"title":1614},"\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":1616,"title":825},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs",{"path":1618,"title":1619},"\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":1621,"title":1622},"\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":1624,"title":1625},"\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":1627,"title":1628},"\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":1630,"title":1631},"\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":1633,"title":1634},"\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":1636,"title":1637},"\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":1639,"title":1640},"\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":1642,"title":1643},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1645,"title":1646},"\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":1648,"title":1649},"\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":1651,"title":1652},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Ffont-display-optional-vs-swap","font-display: optional vs swap",{"path":1654,"title":1655},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1657,"title":1658},"\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":1660,"title":1661},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fpreloading-fonts-without-double-downloads","Preloading Fonts Without Double Downloads",{"path":1663,"title":1664},"\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":1666,"title":1667},"\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":1669,"title":1670},"\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":1672,"title":1673},"\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":1675,"title":1676},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1678,"title":1679},"\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":1681,"title":1682},"\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":1684,"title":1685},"\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":1687,"title":1688},"\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":1690,"title":1691},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1693,"title":1694},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1696,"title":1697},"\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":1699,"title":1700},"\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":1702,"title":1703},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1705,"title":1706},"\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":1708,"title":1709},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Freplacing-react-islands-with-web-components","Replacing React Islands with Web Components",{"path":1711,"title":1712},"\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":1714,"title":1715},"\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":1717,"title":1718},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1720,"title":1721},"\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":1723,"title":1724},"\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":1726,"title":1727},"\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":1729,"title":1730},"\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":1732,"title":1733},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci","Performance Budgets and Lighthouse CI",{"path":1735,"title":1736},"\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":1738,"title":1739},"\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":1741,"title":1742},"\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":1744,"title":1745},"\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":1747,"title":1748},"\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":1750,"title":1751},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fauditing-unused-preloads","Auditing Unused Preloads",{"path":1753,"title":1754},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed","Resource Hints and Navigation Speed",{"path":1756,"title":1757},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Finstant-navigation-with-speculation-rules","Instant Navigation with Speculation Rules",{"path":1759,"title":1760},"\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":1762,"title":1763},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fprefetching-links-in-astro","Prefetching Links in Astro",{"path":1765,"title":1766},"\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":1768,"title":1769},"\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":1771,"title":1772},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1774,"title":1775},"\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":1777,"title":1778},"\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":1780,"title":1781},"\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":1783,"title":1784},"\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":1786,"title":1787},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1789,"title":1790},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages","Configuring Redirects on Cloudflare Pages",{"path":1792,"title":1793},"\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":1795,"title":1796},"\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":1798,"title":1799},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1801,"title":1802},"\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":1804,"title":1805},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests","Checking Links in Pull Requests",{"path":1807,"title":1808},"\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":1810,"title":1811},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci","Editorial Checks with Vale in CI",{"path":1813,"title":1814},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1816,"title":1817},"\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":1819,"title":1820},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build","Wiring a Headless CMS to a Static Build",{"path":1822,"title":1823},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fbuilding-astro-sites-with-github-actions","Building Astro Sites with GitHub Actions",{"path":1825,"title":1826},"\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":1828,"title":1829},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-github-pages-with-actions","Deploying to GitHub Pages with Actions",{"path":1831,"title":1832},"\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":1834,"title":1835},"\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":1837,"title":1838},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1840,"title":1841},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fmatrix-builds-for-multi-site-monorepos","Matrix Builds for Multi-Site Monorepos",{"path":1843,"title":1844},"\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":1846,"title":1847},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1849,"title":1850},"\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":1852,"title":1853},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1855,"title":1856},"\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":1858,"title":1859},"\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":1861,"title":1862},"\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":1864,"title":1865},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1867,"title":1064},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops",{"path":1869,"title":1870},"\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":1872,"title":1873},"\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":1875,"title":1876},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production","Monitoring Static Sites in Production",{"path":1878,"title":1060},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge",{"path":1880,"title":1132},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fuptime-and-synthetic-checks-for-static-sites",{"path":1882,"title":1883},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fconfiguring-vercel-for-hugo-and-eleventy","Configuring Vercel for Hugo and Eleventy",{"path":1885,"title":42},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies",{"path":1887,"title":1888},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1890,"title":1891},"\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":1893,"title":1894},"\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":1896,"title":1897},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":1899,"title":1088},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions",{"path":1901,"title":1099},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments",{"path":1903,"title":1904},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1906,"title":1907},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments","Password-Protecting Preview Deployments",{"path":1909,"title":1910},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts","Previewing Headless CMS Drafts",{"path":1912,"title":1913},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys","Visual Regression Testing on Preview Deploys",{"path":1915,"title":1395},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads",{"path":1917,"title":1918},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fcanary-releases-for-static-sites","Canary Releases for Static Sites",{"path":1920,"title":1921},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Ffeature-flags-on-static-sites","Feature Flags on Static Sites",{"path":1923,"title":1924},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1926,"title":1927},"\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":1929,"title":1930},"\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":1932,"title":1933},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fauditing-npm-dependencies-in-ssg-pipelines","Auditing npm Dependencies in SSG Pipelines",{"path":1935,"title":1936},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fenabling-hsts-and-preload-safely","Enabling HSTS and Preload Safely",{"path":1938,"title":1939},"\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":1941,"title":967},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites",{"path":1943,"title":1142},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc",{"path":1945,"title":1946},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsubresource-integrity-for-third-party-assets","Subresource Integrity for Third-Party Assets",{"path":1948,"title":1949},"\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":1951,"title":421},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3",{"path":1953,"title":552},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects",{"path":1955,"title":547},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fdeploying-a-static-site-to-s3-and-cloudfront",{"path":1443,"title":5},{"path":1958,"title":692},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy",{"path":1960,"title":672},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx",{"path":1962,"title":805},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps",{"path":1964,"title":1965},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fadding-a-contact-form-with-cloudflare-workers","Adding a Contact Form with Cloudflare Workers",{"path":1967,"title":1968},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fhandling-form-submissions-on-a-static-site","Handling Form Submissions on a Static Site",{"path":1970,"title":1971},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites","Serverless Functions for Static Sites",{"path":1973,"title":1974},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers","Netlify Functions vs Cloudflare Workers",{"path":1976,"title":1977},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication","Protecting a Static Site Behind Authentication",{"path":1979,"title":1980},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fproxying-third-party-apis-from-an-edge-function","Proxying Third-Party APIs from an Edge Function",1789722846729]