[{"data":1,"prerenderedAt":1610},["ShallowReactive",2],{"page:\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments":3,"all-docs-nav":1069},{"id":4,"title":5,"body":6,"breadcrumb":1044,"dateModified":1054,"datePublished":1054,"description":1055,"extension":1056,"faq":1057,"meta":1062,"navigation":1063,"path":1064,"seo":1065,"slug":12,"stem":1066,"type":1067,"__hash__":1068},"content\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments\u002Findex.md","Password-Protecting Preview Deployments",{"type":7,"value":8,"toc":1025},"minimark",[9,13,17,26,31,48,52,55,194,220,224,227,274,277,280,284,287,620,623,627,630,674,792,799,803,806,810,817,883,887,896,900,936,940,946,950,955,958,962,965,969,972,976,979,983,1021],[10,11,5],"h1",{"id":12},"password-protecting-preview-deployments",[14,15,16],"p",{},"Preview deployments are one of the best things about modern static hosting: every pull request gets its own URL, reviewers see the real site, and nobody has to run a build locally. They are also a quiet leak. A preview for a launch announcement is live on a public URL days before the launch. It gets pasted into a chat channel with external guests, unfurled with a screenshot, crawled because someone linked it from a public issue, and indexed by a search engine that then shows the unreleased page for weeks.",[14,18,19,20,25],{},"This guide covers the ways to put previews behind authentication — platform-native protection, Cloudflare Access, and a small edge function for hosts that offer neither — plus how automated tests get through and how to keep previews out of search indexes regardless. It is part of ",[21,22,24],"a",{"href":23},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002F","Preview Environments for Pull Requests",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,42,45],"ul",{},[35,36,37,38,25],"li",{},"Preview deployments already running for pull requests — see ",[21,39,41],{"href":40},"\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",[35,43,44],{},"Admin access to the hosting project, and to your identity provider if using SSO.",[35,46,47],{},"A list of who needs access: the team, reviewers in other departments, occasionally external stakeholders.",[27,49,51],{"id":50},"choosing-a-mechanism","Choosing a Mechanism",[14,53,54],{},"The right mechanism depends on how sensitive preview content is and who needs to see it.",[56,57,58,190],"figure",{},[59,60,67,68,67,72,67,76,67,83],"svg",{"viewBox":61,"role":62,"ariaLabelledBy":63,"xmlns":66},"0 0 760 290","img",[64,65],"ppd-opt-title","ppd-opt-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[69,70,71],"title",{"id":64},"Preview protection options by strength and effort",[73,74,75],"desc",{"id":65},"Four options arranged from weakest to strongest. noindex header only: stops indexing, no access control. Shared basic auth password: simple barrier, cannot revoke per person. Platform protection such as Vercel Authentication or Netlify password protection: team login, some plans only. Identity-based access such as Cloudflare Access with SSO: per-person access, audit log, service tokens for CI.",[77,78],"rect",{"x":79,"y":79,"width":80,"height":81,"fill":82},"0","760","290","#ffffff",[84,85,87,88,87,96,87,104,87,109,87,113,87,122,87,129,87,134,87,138,87,145,87,149,87,153,87,156,87,161,87,166,87,170,87,173,87,178,87,183,87,187,67],"g",{"style":86},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[89,90,95],"text",{"x":91,"y":92,"fill":93,"style":94},"380","26","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","From hiding to controlling access",[97,98],"line",{"x1":99,"y1":100,"x2":101,"y2":100,"stroke":102,"style":103},"60","250","720","#556071","stroke-width:1.5px",[89,105,108],{"x":99,"y":106,"fill":102,"style":107},"270","font-size:11px","weaker",[89,110,112],{"x":101,"y":106,"fill":102,"style":111},"font-size:11px;text-anchor:end","stronger",[77,114],{"x":115,"y":116,"width":117,"height":118,"rx":119,"fill":120,"style":121,"stroke":120},"40","170","150","70","8","#d83b41","fill-opacity:0.08",[89,123,128],{"x":124,"y":125,"fill":126,"style":127},"115","194","#b32b30","font-weight:700;text-anchor:middle","noindex only",[89,130,133],{"x":124,"y":131,"fill":93,"style":132},"214","text-anchor:middle","hides from search",[89,135,137],{"x":124,"y":136,"fill":93,"style":132},"230","no access control",[77,139],{"x":140,"y":141,"width":142,"height":143,"rx":119,"fill":144,"style":121,"stroke":144},"210","130","160","80","#8a6d0f",[89,146,148],{"x":81,"y":147,"fill":144,"style":127},"154","shared password",[89,150,152],{"x":81,"y":151,"fill":93,"style":132},"174","basic auth at the edge",[89,154,155],{"x":81,"y":125,"fill":93,"style":132},"no per-person revoke",[77,157],{"x":158,"y":159,"width":142,"height":143,"rx":119,"fill":160,"style":121,"stroke":160},"390","90","#166a9e",[89,162,165],{"x":163,"y":164,"fill":160,"style":127},"470","114","platform protection",[89,167,169],{"x":163,"y":168,"fill":93,"style":132},"134","team login on host",[89,171,172],{"x":163,"y":147,"fill":93,"style":132},"plan-dependent",[77,174],{"x":175,"y":176,"width":142,"height":143,"rx":119,"fill":177,"style":121,"stroke":177},"570","50","#3f6410",[89,179,182],{"x":180,"y":181,"fill":177,"style":127},"650","74","identity access",[89,184,186],{"x":180,"y":185,"fill":93,"style":132},"94","SSO per person",[89,188,189],{"x":180,"y":164,"fill":93,"style":132},"audit log, tokens",[191,192,193],"figcaption",{},"noindex belongs on every preview; the question is what to add on top.",[32,195,196,203,214],{},[35,197,198,202],{},[199,200,201],"strong",{},"Platform protection"," is the least work where available. Vercel's Deployment Protection requires a Vercel login from a team member for previews by default, and offers password protection and trusted IPs on higher plans. Netlify offers site-wide password protection and team login for deploy previews on paid plans. Cloudflare Pages can put preview deployments behind Cloudflare Access with one setting.",[35,204,205,208,209,213],{},[199,206,207],{},"Cloudflare Access"," (or a similar zero-trust proxy) works in front of any host whose previews are on a domain you control, such as ",[210,211,212],"code",{},"*.preview.example.com",". People sign in with the company identity provider, access is granted by group, and every login is logged.",[35,215,216,219],{},[199,217,218],{},"An edge function with basic auth"," is the fallback for hosts with no protection features. It is simple and adequate for low-sensitivity content.",[27,221,223],{"id":222},"cloudflare-access-for-preview-domains","Cloudflare Access for Preview Domains",[14,225,226],{},"If previews live on a wildcard subdomain in a Cloudflare zone, one Access application covers all of them:",[228,229,230,239,257,263],"ol",{},[35,231,232,233,236,237,25],{},"In Zero Trust, create a ",[199,234,235],{},"self-hosted application"," for ",[210,238,212],{},[35,240,241,242,245,246,249,250,253,254,25],{},"Add a policy: ",[199,243,244],{},"Allow"," where the email ends in ",[210,247,248],{},"@example.com",", or where the user belongs to an identity provider group such as ",[210,251,252],{},"engineering"," or ",[210,255,256],{},"content",[35,258,259,260,262],{},"Add a second policy for external reviewers if needed — ",[199,261,244],{}," specific email addresses with one-time PIN login — and give it an expiry.",[35,264,265,266,269,270,273],{},"For Cloudflare Pages previews on ",[210,267,268],{},"*.project.pages.dev",", enable ",[199,271,272],{},"Access policy"," for preview deployments in the project settings instead; it creates the application for you.",[14,275,276],{},"Set the session duration to match how reviewers work — a day is usually comfortable — so people are not asked to sign in for every preview link in a busy review week. Because the application covers the whole wildcard, one login grants access to every preview until the session expires.",[14,278,279],{},"Access sits in front of the preview at the edge. Unauthenticated requests are redirected to the login page and never reach the site, so no preview content — not even its HTML title — is exposed.",[27,281,283],{"id":282},"basic-auth-in-an-edge-function","Basic Auth in an Edge Function",[14,285,286],{},"For hosts without built-in protection, a few lines at the edge add a password. On Cloudflare Workers:",[288,289,294],"pre",{"className":290,"code":291,"language":292,"meta":293,"style":293},"language-ts shiki shiki-themes github-light github-dark","export default {\n  async fetch(request: Request, env: Env) {\n    const url = new URL(request.url);\n    const isPreview = url.hostname !== 'www.example.com';\n    if (isPreview) {\n      const expected = 'Basic ' + btoa(`preview:${env.PREVIEW_PASSWORD}`);\n      if (request.headers.get('Authorization') !== expected) {\n        return new Response('Authentication required', {\n          status: 401,\n          headers: { 'WWW-Authenticate': 'Basic realm=\"Preview\", charset=\"UTF-8\"' },\n        });\n      }\n    }\n    const res = await env.ASSETS.fetch(request);\n    const out = new Response(res.body, res);\n    if (isPreview) out.headers.set('X-Robots-Tag', 'noindex, nofollow');\n    return out;\n  },\n};\n","ts","",[210,295,296,311,348,370,393,402,440,465,484,496,514,520,526,532,559,576,599,608,614],{"__ignoreMap":293},[297,298,300,304,307],"span",{"class":97,"line":299},1,[297,301,303],{"class":302},"szBVR","export",[297,305,306],{"class":302}," default",[297,308,310],{"class":309},"sVt8B"," {\n",[297,312,314,317,321,324,328,331,334,337,340,342,345],{"class":97,"line":313},2,[297,315,316],{"class":302},"  async",[297,318,320],{"class":319},"sScJk"," fetch",[297,322,323],{"class":309},"(",[297,325,327],{"class":326},"s4XuR","request",[297,329,330],{"class":302},":",[297,332,333],{"class":319}," Request",[297,335,336],{"class":309},", ",[297,338,339],{"class":326},"env",[297,341,330],{"class":302},[297,343,344],{"class":319}," Env",[297,346,347],{"class":309},") {\n",[297,349,351,354,358,361,364,367],{"class":97,"line":350},3,[297,352,353],{"class":302},"    const",[297,355,357],{"class":356},"sj4cs"," url",[297,359,360],{"class":302}," =",[297,362,363],{"class":302}," new",[297,365,366],{"class":319}," URL",[297,368,369],{"class":309},"(request.url);\n",[297,371,373,375,378,380,383,386,390],{"class":97,"line":372},4,[297,374,353],{"class":302},[297,376,377],{"class":356}," isPreview",[297,379,360],{"class":302},[297,381,382],{"class":309}," url.hostname ",[297,384,385],{"class":302},"!==",[297,387,389],{"class":388},"sZZnC"," 'www.example.com'",[297,391,392],{"class":309},";\n",[297,394,396,399],{"class":97,"line":395},5,[297,397,398],{"class":302},"    if",[297,400,401],{"class":309}," (isPreview) {\n",[297,403,405,408,411,413,416,419,422,424,427,429,431,434,437],{"class":97,"line":404},6,[297,406,407],{"class":302},"      const",[297,409,410],{"class":356}," expected",[297,412,360],{"class":302},[297,414,415],{"class":388}," 'Basic '",[297,417,418],{"class":302}," +",[297,420,421],{"class":319}," btoa",[297,423,323],{"class":309},[297,425,426],{"class":388},"`preview:${",[297,428,339],{"class":309},[297,430,25],{"class":388},[297,432,433],{"class":356},"PREVIEW_PASSWORD",[297,435,436],{"class":388},"}`",[297,438,439],{"class":309},");\n",[297,441,443,446,449,452,454,457,460,462],{"class":97,"line":442},7,[297,444,445],{"class":302},"      if",[297,447,448],{"class":309}," (request.headers.",[297,450,451],{"class":319},"get",[297,453,323],{"class":309},[297,455,456],{"class":388},"'Authorization'",[297,458,459],{"class":309},") ",[297,461,385],{"class":302},[297,463,464],{"class":309}," expected) {\n",[297,466,468,471,473,476,478,481],{"class":97,"line":467},8,[297,469,470],{"class":302},"        return",[297,472,363],{"class":302},[297,474,475],{"class":319}," Response",[297,477,323],{"class":309},[297,479,480],{"class":388},"'Authentication required'",[297,482,483],{"class":309},", {\n",[297,485,487,490,493],{"class":97,"line":486},9,[297,488,489],{"class":309},"          status: ",[297,491,492],{"class":356},"401",[297,494,495],{"class":309},",\n",[297,497,499,502,505,508,511],{"class":97,"line":498},10,[297,500,501],{"class":309},"          headers: { ",[297,503,504],{"class":388},"'WWW-Authenticate'",[297,506,507],{"class":309},": ",[297,509,510],{"class":388},"'Basic realm=\"Preview\", charset=\"UTF-8\"'",[297,512,513],{"class":309}," },\n",[297,515,517],{"class":97,"line":516},11,[297,518,519],{"class":309},"        });\n",[297,521,523],{"class":97,"line":522},12,[297,524,525],{"class":309},"      }\n",[297,527,529],{"class":97,"line":528},13,[297,530,531],{"class":309},"    }\n",[297,533,535,537,540,542,545,548,551,553,556],{"class":97,"line":534},14,[297,536,353],{"class":302},[297,538,539],{"class":356}," res",[297,541,360],{"class":302},[297,543,544],{"class":302}," await",[297,546,547],{"class":309}," env.",[297,549,550],{"class":356},"ASSETS",[297,552,25],{"class":309},[297,554,555],{"class":319},"fetch",[297,557,558],{"class":309},"(request);\n",[297,560,562,564,567,569,571,573],{"class":97,"line":561},15,[297,563,353],{"class":302},[297,565,566],{"class":356}," out",[297,568,360],{"class":302},[297,570,363],{"class":302},[297,572,475],{"class":319},[297,574,575],{"class":309},"(res.body, res);\n",[297,577,579,581,584,587,589,592,594,597],{"class":97,"line":578},16,[297,580,398],{"class":302},[297,582,583],{"class":309}," (isPreview) out.headers.",[297,585,586],{"class":319},"set",[297,588,323],{"class":309},[297,590,591],{"class":388},"'X-Robots-Tag'",[297,593,336],{"class":309},[297,595,596],{"class":388},"'noindex, nofollow'",[297,598,439],{"class":309},[297,600,602,605],{"class":97,"line":601},17,[297,603,604],{"class":302},"    return",[297,606,607],{"class":309}," out;\n",[297,609,611],{"class":97,"line":610},18,[297,612,613],{"class":309},"  },\n",[297,615,617],{"class":97,"line":616},19,[297,618,619],{"class":309},"};\n",[14,621,622],{},"Netlify Edge Functions and Vercel Middleware can do the same. Store the password as a secret, rotate it when people leave, and compare in constant time if the preview protects anything valuable. Remember that basic auth sends the password with every request, so it must only ever be served over HTTPS.",[27,624,626],{"id":625},"letting-ci-and-bots-through","Letting CI and Bots Through",[14,628,629],{},"Protected previews break the automated checks that run against them — link checks, visual regression tests, Lighthouse — unless they have a way in. Use the mechanism each option provides for automation, never a real person's credentials:",[32,631,632,654,668],{},[35,633,634,637,638,641,642,645,646,649,650,653],{},[199,635,636],{},"Cloudflare Access:"," create a ",[199,639,640],{},"service token"," and add a policy with the ",[199,643,644],{},"Service Auth"," action. CI sends ",[210,647,648],{},"CF-Access-Client-Id"," and ",[210,651,652],{},"CF-Access-Client-Secret"," headers.",[35,655,656,659,660,663,664,667],{},[199,657,658],{},"Vercel:"," generate a ",[199,661,662],{},"Protection Bypass for Automation"," secret; CI sends it in the ",[210,665,666],{},"x-vercel-protection-bypass"," header.",[35,669,670,673],{},[199,671,672],{},"Basic auth:"," give CI its own password or header token, checked separately in the edge function.",[56,675,676,789],{},[59,677,67,682,67,685,67,688,67,690,67,706],{"viewBox":678,"role":62,"ariaLabelledBy":679,"xmlns":66},"0 0 760 250",[680,681],"ppd-flow-title","ppd-flow-desc",[69,683,684],{"id":680},"How people and CI reach a protected preview",[73,686,687],{"id":681},"A reviewer's browser is redirected to the company identity provider, signs in, and receives a session cookie that lets it load the preview. The CI test runner sends a service token in request headers and is allowed through without a login. An anonymous visitor with the URL receives a login page and never sees the preview content.",[77,689],{"x":79,"y":79,"width":80,"height":100,"fill":82},[691,692,87,693,67],"defs",{},[694,695,702],"marker",{"id":696,"viewBox":697,"refX":698,"refY":699,"markerWidth":700,"markerHeight":700,"orient":701},"ppd-flow-arrow","0 0 10 10","9","5","7","auto-start-reverse",[703,704],"path",{"d":705,"fill":102},"M0,0 L10,5 L0,10 z",[84,707,87,708,87,711,87,715,87,720,87,723,87,727,87,730,87,734,87,737,87,742,87,746,87,750,87,754,87,759,87,764,87,766,87,770,87,774,87,777,87,781,87,786,67],{"style":86},[89,709,710],{"x":91,"y":92,"fill":93,"style":94},"Three visitors, one preview URL",[77,712],{"x":713,"y":176,"width":142,"height":115,"rx":119,"fill":160,"style":714,"stroke":160},"20","fill-opacity:0.1",[89,716,719],{"x":717,"y":718,"fill":93,"style":132},"100","75","reviewer (SSO)",[77,721],{"x":713,"y":124,"width":142,"height":115,"rx":119,"fill":722,"style":714,"stroke":722},"#6a4c93",[89,724,726],{"x":717,"y":725,"fill":93,"style":132},"140","CI runner (token)",[77,728],{"x":713,"y":729,"width":142,"height":115,"rx":119,"fill":120,"style":121,"stroke":120},"180",[89,731,733],{"x":717,"y":732,"fill":93,"style":132},"205","anyone with the URL",[77,735],{"x":81,"y":143,"width":116,"height":736,"rx":119,"fill":102,"style":121,"stroke":102},"110",[89,738,741],{"x":739,"y":740,"fill":93,"style":127},"375","108","edge access check",[89,743,745],{"x":739,"y":141,"fill":102,"style":744},"font-size:11px;text-anchor:middle","session cookie?",[89,747,749],{"x":739,"y":748,"fill":102,"style":744},"148","service token?",[89,751,753],{"x":739,"y":752,"fill":102,"style":744},"166","otherwise: login page",[77,755],{"x":756,"y":143,"width":116,"height":757,"rx":119,"fill":177,"style":758,"stroke":177},"560","44","fill-opacity:0.12",[89,760,763],{"x":761,"y":762,"fill":177,"style":127},"645","107","preview content",[77,765],{"x":756,"y":117,"width":116,"height":757,"rx":119,"fill":120,"style":121,"stroke":120},[89,767,769],{"x":761,"y":768,"fill":126,"style":127},"177","login page only",[97,771],{"x1":729,"y1":118,"x2":772,"y2":736,"stroke":102,"style":773},"286","stroke-width:1.5px;marker-end:url(#ppd-flow-arrow)",[97,775],{"x1":729,"y1":776,"x2":772,"y2":776,"stroke":102,"style":773},"135",[97,778],{"x1":729,"y1":779,"x2":772,"y2":780,"stroke":102,"style":773},"200","165",[97,782],{"x1":783,"y1":124,"x2":784,"y2":785,"stroke":177,"style":773},"460","556","102",[97,787],{"x1":783,"y1":780,"x2":784,"y2":788,"stroke":126,"style":773},"172",[191,790,791],{},"Automation gets its own revocable credential; nobody shares a person's login.",[14,793,794,795,25],{},"Store tokens as CI secrets available only to the workflows that need them. Smoke tests against previews are covered in ",[21,796,798],{"href":797},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url\u002F","Running Smoke Tests Against a Preview URL",[27,800,802],{"id":801},"link-unfurling-and-sharing","Link Unfurling and Sharing",[14,804,805],{},"Chat tools fetch URLs to build previews. A protected preview returns a login page, so unfurls show \"Sign in\" rather than the content — which is the point. If reviewers want meaningful unfurls, share screenshots from the visual regression job instead of making the preview public. Similarly, protected previews will not show real Open Graph images when shared; test those on production after launch or with a platform debugger that supports authentication headers.",[27,807,809],{"id":808},"noindex-on-every-preview","Noindex on Every Preview",[14,811,812,813,816],{},"Whatever else you do, send ",[210,814,815],{},"X-Robots-Tag: noindex"," on every preview response and make sure preview builds do not emit a sitemap pointing at production URLs or a canonical tag pointing at themselves. If protection is ever turned off by mistake, noindex keeps the previews out of search results. Also keep previews on a separate hostname from production, never a path, so a single header rule or Access application can cover them without risk to the live site.",[56,818,819,880],{},[59,820,67,825,67,828,67,831,67,834],{"viewBox":821,"role":62,"ariaLabelledBy":822,"xmlns":66},"0 0 760 220",[823,824],"ppd-res-title","ppd-res-desc",[69,826,827],{"id":823},"Indexed preview URLs before and after protection",[73,829,830],{"id":824},"A site search for the preview domain returned 1,240 indexed preview URLs before protection. Eight weeks after adding Cloudflare Access and noindex, it returned 12, and none after twelve weeks.",[77,832],{"x":79,"y":79,"width":80,"height":833,"fill":82},"220",[84,835,87,836,87,840,87,843,87,847,87,851,87,854,87,859,87,863,87,866,87,871,87,873,87,876,67],{"style":86},[89,837,839],{"x":91,"y":838,"fill":93,"style":94},"28","Preview URLs found in a search engine index",[97,841],{"x1":143,"y1":116,"x2":842,"y2":116,"stroke":102,"style":103},"700",[77,844],{"x":725,"y":176,"width":717,"height":845,"fill":120,"style":846},"120","fill-opacity:0.45",[89,848,850],{"x":849,"y":757,"fill":93,"style":132},"190","1,240",[89,852,853],{"x":849,"y":849,"fill":102,"style":132},"before",[77,855],{"x":856,"y":752,"width":717,"height":857,"fill":144,"style":858},"330","4","fill-opacity:0.6",[89,860,862],{"x":91,"y":861,"fill":93,"style":132},"158","12",[89,864,865],{"x":91,"y":849,"fill":102,"style":132},"week 8",[77,867],{"x":868,"y":869,"width":717,"height":870,"fill":177},"520","169","1",[89,872,79],{"x":175,"y":861,"fill":93,"style":132},[89,874,875],{"x":175,"y":849,"fill":102,"style":132},"week 12",[89,877,879],{"x":91,"y":878,"fill":102,"style":744},"212","removal requests were filed for the 40 most sensitive URLs to speed things up",[191,881,882],{},"Indexed previews drain out over weeks once crawlers see noindex and login pages.",[27,884,886],{"id":885},"measured-impact","Measured Impact",[14,888,889,890,892,893,895],{},"A company discovered 1,240 of its preview URLs in a search engine's index, including an unreleased pricing page. It moved previews from the host's public preview domain to ",[210,891,212],{}," behind Cloudflare Access with SSO, added service tokens for its Playwright and link-check jobs, and set ",[210,894,815],{}," on every preview response. Indexed preview URLs fell to 12 after eight weeks and to zero after twelve, helped by removal requests for the most sensitive pages. Reviewers' feedback was that SSO was less friction than the shared password it replaced.",[27,897,899],{"id":898},"pitfalls-rollback","Pitfalls & Rollback",[32,901,902,908,914,920,930],{},[35,903,904,907],{},[199,905,906],{},"Protecting the preview but not its assets."," Images and JSON on another hostname can still leak; cover every preview hostname.",[35,909,910,913],{},[199,911,912],{},"Shared passwords in chat."," They end up everywhere; prefer identity-based access.",[35,915,916,919],{},[199,917,918],{},"Breaking CI."," Add the service token path before turning protection on, and test it against one preview first.",[35,921,922,925,926,929],{},[199,923,924],{},"Previews on production paths."," ",[210,927,928],{},"\u002Fpreview\u002F..."," on the live domain is hard to protect safely; use separate hostnames.",[35,931,932,935],{},[199,933,934],{},"Rollback:"," disable the Access application or the protection setting; noindex remains as a safety net.",[27,937,939],{"id":938},"conclusion","Conclusion",[14,941,942,943,945],{},"Preview deployments should be private by default. Add ",[210,944,815],{}," everywhere, then put previews behind the strongest access control your platform offers — built-in team login, Cloudflare Access with SSO, or at minimum basic auth at the edge — and give automated checks their own revocable tokens. Keep previews on separate hostnames so one rule covers them all, and a leaked preview URL becomes a login page instead of an early launch.",[27,947,949],{"id":948},"faq","FAQ",[951,952,954],"h3",{"id":953},"why-should-preview-deployments-be-protected","Why should preview deployments be protected?",[14,956,957],{},"Previews often contain unreleased features, draft announcements, pricing changes or content under embargo, and their URLs leak through chat, screenshots, referrer headers and link unfurling. Protection stops anyone who finds a URL from reading it and stops search engines from indexing it.",[951,959,961],{"id":960},"is-http-basic-auth-good-enough-for-previews","Is HTTP basic auth good enough for previews?",[14,963,964],{},"For low-sensitivity previews, a shared password over HTTPS is a reasonable barrier. It is weak for anything confidential because the password is shared, rarely rotated and cannot be revoked per person. Use identity-based access such as Cloudflare Access or platform SSO for sensitive content.",[951,966,968],{"id":967},"how-do-automated-tests-reach-a-protected-preview","How do automated tests reach a protected preview?",[14,970,971],{},"Use a bypass mechanism designed for automation - a service token with Cloudflare Access, a protection bypass secret header on Vercel, or a separate credential for basic auth - stored as a CI secret and sent only by the test runner.",[951,973,975],{"id":974},"does-noindex-replace-password-protection","Does noindex replace password protection?",[14,977,978],{},"No. noindex only asks well-behaved crawlers not to index a page; anyone with the URL can still read it. Use both - protection to control access, and an X-Robots-Tag noindex header as a safety net if protection is ever turned off.",[27,980,982],{"id":981},"related","Related",[32,984,985,993,1000,1007,1014],{},[35,986,987,925,990,992],{},[199,988,989],{},"Parent:",[21,991,24],{"href":23}," — the full preview workflow.",[35,994,995,999],{},[21,996,998],{"href":997},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments\u002F","Cleaning Up Stale Preview Deployments"," — fewer previews to protect.",[35,1001,1002,1006],{},[21,1003,1005],{"href":1004},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts\u002F","Previewing Headless CMS Drafts"," — where the most sensitive content appears.",[35,1008,1009,1013],{},[21,1010,1012],{"href":1011},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication\u002F","Protecting a Static Site Behind Authentication"," — the same techniques for production.",[35,1015,1016,1020],{},[21,1017,1019],{"href":1018},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys\u002F","Visual Regression Testing on Preview Deploys"," — tests that need the bypass token.",[1022,1023,1024],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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":293,"searchDepth":313,"depth":313,"links":1026},[1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1043],{"id":29,"depth":313,"text":30},{"id":50,"depth":313,"text":51},{"id":222,"depth":313,"text":223},{"id":282,"depth":313,"text":283},{"id":625,"depth":313,"text":626},{"id":801,"depth":313,"text":802},{"id":808,"depth":313,"text":809},{"id":885,"depth":313,"text":886},{"id":898,"depth":313,"text":899},{"id":938,"depth":313,"text":939},{"id":948,"depth":313,"text":949,"children":1038},[1039,1040,1041,1042],{"id":953,"depth":350,"text":954},{"id":960,"depth":350,"text":961},{"id":967,"depth":350,"text":968},{"id":974,"depth":350,"text":975},{"id":981,"depth":313,"text":982},[1045,1048,1051,1052],{"name":1046,"item":1047},"Home","\u002F",{"name":1049,"item":1050},"Production-Ready Deployment & CI\u002FCD Workflows","\u002Fproduction-ready-deployment-cicd-workflows\u002F",{"name":24,"item":23},{"name":5,"item":1053},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments\u002F","2026-09-18","Keep pull request previews private with platform protection, Cloudflare Access or a small edge function, and keep them out of search indexes with noindex.","md",[1058,1059,1060,1061],{"q":954,"a":957},{"q":961,"a":964},{"q":968,"a":971},{"q":975,"a":978},{},true,"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments",{"title":5,"description":1055},"production-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments\u002Findex","article","L2j1A01OIGEC0txeaLP5tHi_ASd2Qwsjxa7q2Kftf-Y",[1070,1073,1076,1079,1082,1085,1088,1091,1094,1097,1100,1103,1106,1109,1112,1115,1118,1121,1124,1127,1130,1133,1136,1139,1142,1145,1148,1151,1154,1157,1160,1163,1166,1169,1172,1175,1178,1181,1184,1187,1190,1193,1196,1199,1202,1205,1208,1211,1214,1217,1220,1223,1226,1229,1232,1235,1238,1241,1244,1247,1250,1253,1256,1259,1262,1265,1268,1271,1274,1277,1280,1283,1286,1289,1292,1295,1298,1301,1304,1307,1310,1313,1316,1319,1322,1325,1328,1331,1334,1337,1340,1343,1346,1349,1352,1355,1358,1361,1364,1367,1370,1373,1376,1379,1382,1385,1388,1391,1394,1397,1400,1403,1406,1409,1412,1415,1418,1421,1424,1427,1430,1433,1436,1439,1442,1445,1448,1451,1454,1457,1460,1463,1466,1469,1472,1475,1478,1481,1484,1487,1490,1493,1496,1499,1502,1505,1508,1511,1514,1517,1520,1523,1525,1527,1529,1530,1532,1534,1537,1540,1543,1546,1549,1551,1554,1557,1560,1563,1566,1569,1572,1575,1578,1581,1584,1587,1590,1593,1596,1599,1602,1605,1607],{"path":1071,"title":1072},"\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":1074,"title":1075},"\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":1077,"title":1078},"\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":1080,"title":1081},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1083,"title":1084},"\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":1086,"title":1087},"\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":1089,"title":1090},"\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":1092,"title":1093},"\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":1095,"title":1096},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress","Docs Frameworks: Docusaurus, Starlight and VitePress",{"path":1098,"title":1099},"\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":1101,"title":1102},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight","Migrating from MkDocs to Starlight",{"path":1104,"title":1105},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fversioned-documentation-with-docusaurus","Versioned Documentation with Docusaurus",{"path":1107,"title":1108},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fvitepress-for-library-documentation","VitePress for Library Documentation",{"path":1110,"title":1111},"\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":1113,"title":1114},"\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":1116,"title":1117},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1119,"title":1120},"\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":1122,"title":1123},"\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":1125,"title":1126},"\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":1128,"title":1129},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1131,"title":1132},"\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":1134,"title":1135},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1137,"title":1138},"\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":1140,"title":1141},"\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":1143,"title":1144},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fspeeding-up-slow-jekyll-builds","Speeding Up Slow Jekyll Builds",{"path":1146,"title":1147},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fupgrading-jekyll-and-ruby-versions-safely","Upgrading Jekyll and Ruby Versions Safely",{"path":1149,"title":1150},"\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":1152,"title":1153},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1155,"title":1156},"\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":1158,"title":1159},"\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":1161,"title":1162},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-gatsby-to-astro","Migrating from Gatsby to Astro",{"path":1164,"title":1165},"\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":1167,"title":1168},"\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":1170,"title":1171},"\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":1173,"title":1174},"\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":1176,"title":1177},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1179,"title":1180},"\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":1182,"title":1183},"\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":1185,"title":1186},"\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":1188,"title":1189},"\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":1191,"title":1192},"\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":1194,"title":1195},"\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":1197,"title":1198},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites","Search for Static Sites",{"path":1200,"title":1201},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Findexing-hugo-sites-with-pagefind","Indexing Hugo Sites with Pagefind",{"path":1203,"title":1204},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fmultilingual-search-on-static-sites","Multilingual Search on Static Sites",{"path":1206,"title":1207},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fpagefind-vs-algolia-docsearch","Pagefind vs Algolia DocSearch",{"path":1209,"title":1210},"\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":1212,"title":1213},"\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":1215,"title":1216},"\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":1218,"title":1219},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fevaluating-ssg-accessibility-defaults","Evaluating SSG Accessibility Defaults",{"path":1221,"title":1222},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1224,"title":1225},"\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":1227,"title":1228},"\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":1230,"title":1231},"\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":1233,"title":1234},"\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":1236,"title":1237},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1239,"title":1240},"\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":1242,"title":1243},"\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":1245,"title":1246},"\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":1248,"title":1249},"\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":1251,"title":1252},"\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":1254,"title":1255},"\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":1257,"title":1258},"\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":1260,"title":1261},"\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":1263,"title":1264},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1266,"title":1267},"\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":1269,"title":1270},"\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":1272,"title":1273},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Ffont-display-optional-vs-swap","font-display: optional vs swap",{"path":1275,"title":1276},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1278,"title":1279},"\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":1281,"title":1282},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fpreloading-fonts-without-double-downloads","Preloading Fonts Without Double Downloads",{"path":1284,"title":1285},"\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":1287,"title":1288},"\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":1290,"title":1291},"\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":1293,"title":1294},"\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":1296,"title":1297},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1299,"title":1300},"\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":1302,"title":1303},"\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":1305,"title":1306},"\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":1308,"title":1309},"\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":1311,"title":1312},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1314,"title":1315},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1317,"title":1318},"\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":1320,"title":1321},"\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":1323,"title":1324},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1326,"title":1327},"\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":1329,"title":1330},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Freplacing-react-islands-with-web-components","Replacing React Islands with Web Components",{"path":1332,"title":1333},"\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":1335,"title":1336},"\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":1338,"title":1339},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1341,"title":1342},"\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":1344,"title":1345},"\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":1347,"title":1348},"\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":1350,"title":1351},"\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":1353,"title":1354},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci","Performance Budgets and Lighthouse CI",{"path":1356,"title":1357},"\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":1359,"title":1360},"\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":1362,"title":1363},"\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":1365,"title":1366},"\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":1368,"title":1369},"\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":1371,"title":1372},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fauditing-unused-preloads","Auditing Unused Preloads",{"path":1374,"title":1375},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed","Resource Hints and Navigation Speed",{"path":1377,"title":1378},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Finstant-navigation-with-speculation-rules","Instant Navigation with Speculation Rules",{"path":1380,"title":1381},"\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":1383,"title":1384},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fprefetching-links-in-astro","Prefetching Links in Astro",{"path":1386,"title":1387},"\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":1389,"title":1390},"\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":1392,"title":1393},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1395,"title":1396},"\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":1398,"title":1399},"\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":1401,"title":1402},"\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":1404,"title":1405},"\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":1407,"title":1408},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1410,"title":1411},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages","Configuring Redirects on Cloudflare Pages",{"path":1413,"title":1414},"\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":1416,"title":1417},"\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":1419,"title":1420},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1422,"title":1423},"\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":1425,"title":1426},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests","Checking Links in Pull Requests",{"path":1428,"title":1429},"\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":1431,"title":1432},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci","Editorial Checks with Vale in CI",{"path":1434,"title":1435},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1437,"title":1438},"\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":1440,"title":1441},"\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":1443,"title":1444},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fbuilding-astro-sites-with-github-actions","Building Astro Sites with GitHub Actions",{"path":1446,"title":1447},"\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":1449,"title":1450},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-github-pages-with-actions","Deploying to GitHub Pages with Actions",{"path":1452,"title":1453},"\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":1455,"title":1456},"\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":1458,"title":1459},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1461,"title":1462},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fmatrix-builds-for-multi-site-monorepos","Matrix Builds for Multi-Site Monorepos",{"path":1464,"title":1465},"\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":1467,"title":1468},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1470,"title":1471},"\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":1473,"title":1474},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1476,"title":1477},"\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":1479,"title":1480},"\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":1482,"title":1483},"\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":1485,"title":1486},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1488,"title":1489},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops","Alerting on Cache Hit Ratio Drops",{"path":1491,"title":1492},"\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":1494,"title":1495},"\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":1497,"title":1498},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production","Monitoring Static Sites in Production",{"path":1500,"title":1501},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge","Logging 404s at the Edge",{"path":1503,"title":1504},"\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":1506,"title":1507},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fconfiguring-vercel-for-hugo-and-eleventy","Configuring Vercel for Hugo and Eleventy",{"path":1509,"title":1510},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1512,"title":1513},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1515,"title":1516},"\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":1518,"title":1519},"\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":1521,"title":1522},"\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":1524,"title":41},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions",{"path":1526,"title":998},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments",{"path":1528,"title":24},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests",{"path":1064,"title":5},{"path":1531,"title":1005},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts",{"path":1533,"title":1019},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys",{"path":1535,"title":1536},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1538,"title":1539},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fcanary-releases-for-static-sites","Canary Releases for Static Sites",{"path":1541,"title":1542},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Ffeature-flags-on-static-sites","Feature Flags on Static Sites",{"path":1544,"title":1545},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1547,"title":1548},"\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":1550,"title":798},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url",{"path":1552,"title":1553},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fauditing-npm-dependencies-in-ssg-pipelines","Auditing npm Dependencies in SSG Pipelines",{"path":1555,"title":1556},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fenabling-hsts-and-preload-safely","Enabling HSTS and Preload Safely",{"path":1558,"title":1559},"\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":1561,"title":1562},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites","Security Headers and Hardening for Static Sites",{"path":1564,"title":1565},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc","Securing Deploy Credentials with GitHub OIDC",{"path":1567,"title":1568},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsubresource-integrity-for-third-party-assets","Subresource Integrity for Third-Party Assets",{"path":1570,"title":1571},"\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":1573,"title":1574},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3","Clean URLs and Trailing Slashes on S3",{"path":1576,"title":1577},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects","CloudFront Functions for Redirects",{"path":1579,"title":1580},"\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":1582,"title":1583},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites","Self-Hosting Static Sites on S3, Nginx and Caddy",{"path":1585,"title":1586},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy","Serving a Static Site with Caddy",{"path":1588,"title":1589},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx","Serving a Static Site with Nginx",{"path":1591,"title":1592},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps","Zero-Downtime Deploys with Symlink Swaps",{"path":1594,"title":1595},"\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":1597,"title":1598},"\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":1600,"title":1601},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites","Serverless Functions for Static Sites",{"path":1603,"title":1604},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers","Netlify Functions vs Cloudflare Workers",{"path":1606,"title":1012},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication",{"path":1608,"title":1609},"\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",1789722847494]