[{"data":1,"prerenderedAt":1666},["ShallowReactive",2],{"page:\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests":3,"all-docs-nav":1126},{"id":4,"title":5,"body":6,"breadcrumb":1102,"dateModified":1112,"datePublished":1112,"description":1113,"extension":1114,"faq":1115,"meta":1120,"navigation":356,"path":1121,"seo":1122,"slug":12,"stem":1123,"type":1124,"__hash__":1125},"content\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests\u002Findex.md","Checking Links in Pull Requests",{"type":7,"value":8,"toc":1083},"minimark",[9,13,17,26,31,52,56,59,164,167,171,179,250,264,267,305,315,319,322,682,689,693,696,726,827,831,841,913,920,924,936,942,946,949,953,998,1002,1005,1009,1014,1017,1021,1024,1028,1031,1035,1038,1042,1079],[10,11,5],"h1",{"id":12},"checking-links-in-pull-requests",[14,15,16],"p",{},"Broken links accumulate quietly on documentation sites. A page is renamed and three guides still link to the old path. A heading is reworded and every deep link to its anchor lands at the top of the page instead. A section is deleted and the sidebar still lists it. Readers notice long before anyone on the team does, and scheduled crawls of production only find the damage after it has shipped.",[14,18,19,20,25],{},"Checking links in every pull request stops most of it at the source. This guide builds the site in CI, checks internal links and heading anchors in the output with lychee as a blocking check, and handles external links separately so they never make builds flaky. It is part of ",[21,22,24],"a",{"href":23},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002F","Content Workflows for Documentation Teams",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,42,45],"ul",{},[35,36,37,38,25],"li",{},"A static site that builds in CI — see ",[21,39,41],{"href":40},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002F","GitHub Actions for Automated SSG Builds",[35,43,44],{},"Pull requests as the way changes reach production.",[35,46,47,48,25],{},"Optional: an existing scheduled crawl of production for comparison, as in ",[21,49,51],{"href":50},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fcrawling-for-broken-links-on-a-schedule\u002F","Crawling for Broken Links on a Schedule",[27,53,55],{"id":54},"check-the-build-not-the-source","Check the Build, Not the Source",[14,57,58],{},"Markdown links are only part of the picture. The generator adds navigation, breadcrumbs, pagination, tag pages and \"next page\" links; it rewrites relative links, applies a base path, and turns headings into anchor IDs with its own slug rules. A checker that reads Markdown sees none of that. A checker that reads the built HTML sees exactly what readers see.",[60,61,62,160],"figure",{},[63,64,71,72,71,76,71,80,71,87],"svg",{"viewBox":65,"role":66,"ariaLabelledBy":67,"xmlns":70},"0 0 760 260","img",[68,69],"clp-src-title","clp-src-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[73,74,75],"title",{"id":68},"What a source check and a build check can see",[77,78,79],"desc",{"id":69},"A source check sees links written in Markdown content only. A build check sees content links plus navigation, breadcrumbs, generated index pages, base path rewriting and heading anchor IDs created by the generator.",[81,82],"rect",{"x":83,"y":83,"width":84,"height":85,"fill":86},"0","760","260","#ffffff",[88,89,91,92,91,100,91,109,91,115,91,120,91,125,91,129,91,133,91,137,91,141,91,145,91,148,91,151,91,154,91,157,71],"g",{"style":90},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[93,94,99],"text",{"x":95,"y":96,"fill":97,"style":98},"380","26","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Coverage of each approach",[81,101],{"x":102,"y":103,"width":104,"height":105,"rx":106,"fill":107,"style":108,"stroke":107},"30","46","330","190","8","#8a6d0f","fill-opacity:0.08",[93,110,114],{"x":111,"y":112,"fill":107,"style":113},"195","72","font-weight:700;text-anchor:middle","Markdown source check",[93,116,119],{"x":117,"y":118,"fill":97},"48","104","✓ links written in content",[93,121,124],{"x":117,"y":122,"fill":123},"130","#556071","✗ sidebar and header navigation",[93,126,128],{"x":117,"y":127,"fill":123},"156","✗ generated tag and index pages",[93,130,132],{"x":117,"y":131,"fill":123},"182","✗ base path and link rewriting",[93,134,136],{"x":117,"y":135,"fill":123},"208","✗ generator's heading anchor IDs",[81,138],{"x":139,"y":103,"width":104,"height":105,"rx":106,"fill":140,"style":108,"stroke":140},"400","#3f6410",[93,142,144],{"x":143,"y":112,"fill":140,"style":113},"565","Built HTML check",[93,146,119],{"x":147,"y":118,"fill":97},"418",[93,149,150],{"x":147,"y":122,"fill":97},"✓ sidebar and header navigation",[93,152,153],{"x":147,"y":127,"fill":97},"✓ generated tag and index pages",[93,155,156],{"x":147,"y":131,"fill":97},"✓ base path and link rewriting",[93,158,159],{"x":147,"y":135,"fill":97},"✓ generator's heading anchor IDs",[161,162,163],"figcaption",{},"The build is the product; check the product.",[14,165,166],{},"The build takes longer than reading Markdown, but most CI pipelines already build the site for a preview deployment. The link check reuses that output.",[27,168,170],{"id":169},"setting-up-lychee","Setting Up lychee",[14,172,173,174,178],{},"lychee checks local HTML files and remote URLs concurrently. Put its settings in ",[175,176,177],"code",{},"lychee.toml"," at the repository root so local runs and CI behave the same:",[180,181,186],"pre",{"className":182,"code":183,"language":184,"meta":185,"style":185},"language-toml shiki shiki-themes github-light github-dark","# lychee.toml\nroot_dir = \"dist\"\ninclude_fragments = true\nexclude_path = [\"dist\u002F404.html\"]\nexclude = [\n  \"^https:\u002F\u002Fexample\\\\.com\u002F\",     # our own production domain: checked as local files\n  \"^mailto:\",\n]\nmax_concurrency = 32\naccept = [\"200..=299\", \"429\"]\n","toml","",[175,187,188,196,202,208,214,220,226,232,238,244],{"__ignoreMap":185},[189,190,193],"span",{"class":191,"line":192},"line",1,[189,194,195],{},"# lychee.toml\n",[189,197,199],{"class":191,"line":198},2,[189,200,201],{},"root_dir = \"dist\"\n",[189,203,205],{"class":191,"line":204},3,[189,206,207],{},"include_fragments = true\n",[189,209,211],{"class":191,"line":210},4,[189,212,213],{},"exclude_path = [\"dist\u002F404.html\"]\n",[189,215,217],{"class":191,"line":216},5,[189,218,219],{},"exclude = [\n",[189,221,223],{"class":191,"line":222},6,[189,224,225],{},"  \"^https:\u002F\u002Fexample\\\\.com\u002F\",     # our own production domain: checked as local files\n",[189,227,229],{"class":191,"line":228},7,[189,230,231],{},"  \"^mailto:\",\n",[189,233,235],{"class":191,"line":234},8,[189,236,237],{},"]\n",[189,239,241],{"class":191,"line":240},9,[189,242,243],{},"max_concurrency = 32\n",[189,245,247],{"class":191,"line":246},10,[189,248,249],{},"accept = [\"200..=299\", \"429\"]\n",[14,251,252,255,256,259,260,263],{},[175,253,254],{},"root_dir"," lets lychee resolve root-relative links such as ",[175,257,258],{},"\u002Fdocs\u002Finstall\u002F"," against the build output. ",[175,261,262],{},"include_fragments"," checks that anchors exist. Excluding your own production domain avoids checking absolute self-links against the live site, which would miss pages added in the pull request.",[14,265,266],{},"Run it locally after a build:",[180,268,272],{"className":269,"code":270,"language":271,"meta":185,"style":185},"language-bash shiki shiki-themes github-light github-dark","npm run build\nlychee --config lychee.toml --offline 'dist\u002F**\u002F*.html'\n","bash",[175,273,274,287],{"__ignoreMap":185},[189,275,276,280,284],{"class":191,"line":192},[189,277,279],{"class":278},"sScJk","npm",[189,281,283],{"class":282},"sZZnC"," run",[189,285,286],{"class":282}," build\n",[189,288,289,292,296,299,302],{"class":191,"line":198},[189,290,291],{"class":278},"lychee",[189,293,295],{"class":294},"sj4cs"," --config",[189,297,298],{"class":282}," lychee.toml",[189,300,301],{"class":294}," --offline",[189,303,304],{"class":282}," 'dist\u002F**\u002F*.html'\n",[14,306,307,310,311,314],{},[175,308,309],{},"--offline"," skips all remote URLs, which is the fast, deterministic check suited to every pull request. On a 2,000-page site it completes in around 20 seconds, less than the build itself, so there is no reason to skip it for small content changes. Add it as an npm script such as ",[175,312,313],{},"npm run check:links"," so writers can run exactly the same command before pushing.",[27,316,318],{"id":317},"the-pull-request-workflow","The Pull Request Workflow",[14,320,321],{},"Two jobs: a blocking internal check and a non-blocking external check.",[180,323,327],{"className":324,"code":325,"language":326,"meta":185,"style":185},"language-yaml shiki shiki-themes github-light github-dark","name: Links\non: pull_request\n\njobs:\n  internal:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-node@v4\n        with: { node-version: 22, cache: npm }\n      - run: npm ci && npm run build\n      - uses: lycheeverse\u002Flychee-action@v2\n        with:\n          args: --config lychee.toml --offline 'dist\u002F**\u002F*.html'\n          fail: true\n\n  external:\n    runs-on: ubuntu-latest\n    continue-on-error: true\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-node@v4\n        with: { node-version: 22, cache: npm }\n      - run: npm ci && npm run build\n      - uses: actions\u002Fcache@v4\n        with:\n          path: .lycheecache\n          key: lychee-${{ github.sha }}\n          restore-keys: lychee-\n      - uses: lycheeverse\u002Flychee-action@v2\n        with:\n          args: --config lychee.toml --cache --max-cache-age 3d --scheme https 'dist\u002F**\u002F*.html'\n          fail: false\n","yaml",[175,328,329,342,352,358,366,373,383,390,403,414,443,456,468,475,486,497,502,510,519,529,536,547,558,581,592,604,611,622,633,644,655,662,672],{"__ignoreMap":185},[189,330,331,335,339],{"class":191,"line":192},[189,332,334],{"class":333},"s9eBZ","name",[189,336,338],{"class":337},"sVt8B",": ",[189,340,341],{"class":282},"Links\n",[189,343,344,347,349],{"class":191,"line":198},[189,345,346],{"class":294},"on",[189,348,338],{"class":337},[189,350,351],{"class":282},"pull_request\n",[189,353,354],{"class":191,"line":204},[189,355,357],{"emptyLinePlaceholder":356},true,"\n",[189,359,360,363],{"class":191,"line":210},[189,361,362],{"class":333},"jobs",[189,364,365],{"class":337},":\n",[189,367,368,371],{"class":191,"line":216},[189,369,370],{"class":333},"  internal",[189,372,365],{"class":337},[189,374,375,378,380],{"class":191,"line":222},[189,376,377],{"class":333},"    runs-on",[189,379,338],{"class":337},[189,381,382],{"class":282},"ubuntu-latest\n",[189,384,385,388],{"class":191,"line":228},[189,386,387],{"class":333},"    steps",[189,389,365],{"class":337},[189,391,392,395,398,400],{"class":191,"line":234},[189,393,394],{"class":337},"      - ",[189,396,397],{"class":333},"uses",[189,399,338],{"class":337},[189,401,402],{"class":282},"actions\u002Fcheckout@v4\n",[189,404,405,407,409,411],{"class":191,"line":240},[189,406,394],{"class":337},[189,408,397],{"class":333},[189,410,338],{"class":337},[189,412,413],{"class":282},"actions\u002Fsetup-node@v4\n",[189,415,416,419,422,425,427,430,433,436,438,440],{"class":191,"line":246},[189,417,418],{"class":333},"        with",[189,420,421],{"class":337},": { ",[189,423,424],{"class":333},"node-version",[189,426,338],{"class":337},[189,428,429],{"class":294},"22",[189,431,432],{"class":337},", ",[189,434,435],{"class":333},"cache",[189,437,338],{"class":337},[189,439,279],{"class":282},[189,441,442],{"class":337}," }\n",[189,444,446,448,451,453],{"class":191,"line":445},11,[189,447,394],{"class":337},[189,449,450],{"class":333},"run",[189,452,338],{"class":337},[189,454,455],{"class":282},"npm ci && npm run build\n",[189,457,459,461,463,465],{"class":191,"line":458},12,[189,460,394],{"class":337},[189,462,397],{"class":333},[189,464,338],{"class":337},[189,466,467],{"class":282},"lycheeverse\u002Flychee-action@v2\n",[189,469,471,473],{"class":191,"line":470},13,[189,472,418],{"class":333},[189,474,365],{"class":337},[189,476,478,481,483],{"class":191,"line":477},14,[189,479,480],{"class":333},"          args",[189,482,338],{"class":337},[189,484,485],{"class":282},"--config lychee.toml --offline 'dist\u002F**\u002F*.html'\n",[189,487,489,492,494],{"class":191,"line":488},15,[189,490,491],{"class":333},"          fail",[189,493,338],{"class":337},[189,495,496],{"class":294},"true\n",[189,498,500],{"class":191,"line":499},16,[189,501,357],{"emptyLinePlaceholder":356},[189,503,505,508],{"class":191,"line":504},17,[189,506,507],{"class":333},"  external",[189,509,365],{"class":337},[189,511,513,515,517],{"class":191,"line":512},18,[189,514,377],{"class":333},[189,516,338],{"class":337},[189,518,382],{"class":282},[189,520,522,525,527],{"class":191,"line":521},19,[189,523,524],{"class":333},"    continue-on-error",[189,526,338],{"class":337},[189,528,496],{"class":294},[189,530,532,534],{"class":191,"line":531},20,[189,533,387],{"class":333},[189,535,365],{"class":337},[189,537,539,541,543,545],{"class":191,"line":538},21,[189,540,394],{"class":337},[189,542,397],{"class":333},[189,544,338],{"class":337},[189,546,402],{"class":282},[189,548,550,552,554,556],{"class":191,"line":549},22,[189,551,394],{"class":337},[189,553,397],{"class":333},[189,555,338],{"class":337},[189,557,413],{"class":282},[189,559,561,563,565,567,569,571,573,575,577,579],{"class":191,"line":560},23,[189,562,418],{"class":333},[189,564,421],{"class":337},[189,566,424],{"class":333},[189,568,338],{"class":337},[189,570,429],{"class":294},[189,572,432],{"class":337},[189,574,435],{"class":333},[189,576,338],{"class":337},[189,578,279],{"class":282},[189,580,442],{"class":337},[189,582,584,586,588,590],{"class":191,"line":583},24,[189,585,394],{"class":337},[189,587,450],{"class":333},[189,589,338],{"class":337},[189,591,455],{"class":282},[189,593,595,597,599,601],{"class":191,"line":594},25,[189,596,394],{"class":337},[189,598,397],{"class":333},[189,600,338],{"class":337},[189,602,603],{"class":282},"actions\u002Fcache@v4\n",[189,605,607,609],{"class":191,"line":606},26,[189,608,418],{"class":333},[189,610,365],{"class":337},[189,612,614,617,619],{"class":191,"line":613},27,[189,615,616],{"class":333},"          path",[189,618,338],{"class":337},[189,620,621],{"class":282},".lycheecache\n",[189,623,625,628,630],{"class":191,"line":624},28,[189,626,627],{"class":333},"          key",[189,629,338],{"class":337},[189,631,632],{"class":282},"lychee-${{ github.sha }}\n",[189,634,636,639,641],{"class":191,"line":635},29,[189,637,638],{"class":333},"          restore-keys",[189,640,338],{"class":337},[189,642,643],{"class":282},"lychee-\n",[189,645,647,649,651,653],{"class":191,"line":646},30,[189,648,394],{"class":337},[189,650,397],{"class":333},[189,652,338],{"class":337},[189,654,467],{"class":282},[189,656,658,660],{"class":191,"line":657},31,[189,659,418],{"class":333},[189,661,365],{"class":337},[189,663,665,667,669],{"class":191,"line":664},32,[189,666,480],{"class":333},[189,668,338],{"class":337},[189,670,671],{"class":282},"--config lychee.toml --cache --max-cache-age 3d --scheme https 'dist\u002F**\u002F*.html'\n",[189,673,675,677,679],{"class":191,"line":674},33,[189,676,491],{"class":333},[189,678,338],{"class":337},[189,680,681],{"class":294},"false\n",[14,683,684,685,688],{},"Mark only the ",[175,686,687],{},"internal"," job as required in branch protection. Internal failures are always caused by the change or by something the team controls, so blocking on them is fair. External failures are often not, so they are reported but never block.",[27,690,692],{"id":691},"handling-external-links","Handling External Links",[14,694,695],{},"External links fail for reasons unrelated to your change: rate limits, bot protection, slow servers, temporary outages. Three practices keep them useful without noise:",[697,698,699,710,716],"ol",{},[35,700,701,705,706,709],{},[702,703,704],"strong",{},"Cache results."," lychee's ",[175,707,708],{},"--cache"," with a maximum age means a URL checked successfully in the last three days is not requested again, which cuts both run time and rate limiting.",[35,711,712,715],{},[702,713,714],{},"Accept 429."," \"Too many requests\" means the link exists; treat it as success.",[35,717,718,721,722,725],{},[702,719,720],{},"Check on a schedule too."," A nightly run on ",[175,723,724],{},"main"," with retries catches external links that died since they were added, and can open an issue with the list instead of failing a pull request.",[60,727,728,824],{},[63,729,71,734,71,737,71,740,71,743],{"viewBox":730,"role":66,"ariaLabelledBy":731,"xmlns":70},"0 0 760 250",[732,733],"clp-split-title","clp-split-desc",[73,735,736],{"id":732},"Blocking and non-blocking link checks",[77,738,739],{"id":733},"On each pull request, an offline internal check with anchors runs in about 20 seconds and blocks merge on failure. A cached external check runs alongside and only reports. Nightly on main, a full external check with retries opens an issue listing dead links.",[81,741],{"x":83,"y":83,"width":84,"height":742,"fill":86},"250",[88,744,91,745,91,748,91,756,91,762,91,767,91,770,91,774,91,779,91,782,91,785,91,789,91,792,91,795,91,798,91,802,91,806,91,810,91,813,91,816,91,819,71],{"style":90},[93,746,747],{"x":95,"y":96,"fill":97,"style":98},"Three checks, three purposes",[81,749],{"x":750,"y":751,"width":752,"height":753,"rx":106,"fill":754,"style":755,"stroke":754},"20","50","230","170","#d83b41","fill-opacity:0.06",[93,757,761],{"x":758,"y":759,"fill":760,"style":113},"135","76","#b32b30","PR · internal",[93,763,766],{"x":764,"y":765,"fill":97},"36","106","offline, with anchors",[93,768,769],{"x":764,"y":122,"fill":97},"≈ 20 s for 2,000 pages",[93,771,773],{"x":764,"y":772,"fill":97},"154","deterministic",[93,775,778],{"x":764,"y":776,"fill":760,"style":777},"196","font-weight:700","blocks merge",[81,780],{"x":781,"y":751,"width":752,"height":753,"rx":106,"fill":107,"style":755,"stroke":107},"265",[93,783,784],{"x":95,"y":759,"fill":107,"style":113},"PR · external",[93,786,788],{"x":787,"y":765,"fill":97},"281","remote URLs, cached 3 days",[93,790,791],{"x":787,"y":122,"fill":97},"429 accepted",[93,793,794],{"x":787,"y":772,"fill":97},"can be flaky",[93,796,797],{"x":787,"y":776,"fill":107,"style":777},"reports only",[81,799],{"x":800,"y":751,"width":752,"height":753,"rx":106,"fill":801,"style":755,"stroke":801},"510","#166a9e",[93,803,805],{"x":804,"y":759,"fill":801,"style":113},"625","Nightly · main",[93,807,809],{"x":808,"y":765,"fill":97},"526","all external URLs",[93,811,812],{"x":808,"y":122,"fill":97},"retries, longer timeout",[93,814,815],{"x":808,"y":772,"fill":97},"catches link rot",[93,817,818],{"x":808,"y":776,"fill":801,"style":777},"opens an issue",[93,820,823],{"x":95,"y":821,"fill":123,"style":822},"242","font-size:11px;text-anchor:middle","only failures the pull request can have caused are allowed to block it",[161,825,826],{},"Separating the checks keeps the blocking one fast and trustworthy.",[27,828,830],{"id":829},"anchors-and-renamed-headings","Anchors and Renamed Headings",[14,832,833,834,837,838,840],{},"Heading anchors are the most fragile links on a documentation site. A writer changes \"Install requirements\" to \"System requirements\" and every link to ",[175,835,836],{},"#install-requirements"," silently lands at the top of the page. With ",[175,839,262],{},", lychee reports each one on the pull request that renamed the heading, when the fix is a quick search and replace.",[60,842,843,910],{},[63,844,71,849,71,852,71,855,71,857],{"viewBox":845,"role":66,"ariaLabelledBy":846,"xmlns":70},"0 0 760 230",[847,848],"clp-cause-title","clp-cause-desc",[73,850,851],{"id":847},"Causes of internal link failures blocked in the first month",[77,853,854],{"id":848},"Of 23 pull requests blocked by the internal link check, 17 were anchors broken by renamed headings, 4 were links to moved or deleted pages, and 2 were typos in hand-written paths.",[81,856],{"x":83,"y":83,"width":84,"height":752,"fill":86},[88,858,91,859,91,863,91,868,91,875,91,880,91,885,91,888,91,893,91,898,91,901,91,906,71],{"style":90},[93,860,862],{"x":95,"y":861,"fill":97,"style":98},"28","Why 23 pull requests were blocked",[93,864,867],{"x":865,"y":866,"fill":97},"75","74","anchor broken by heading edit",[81,869],{"x":870,"y":871,"width":872,"height":750,"fill":873,"style":874},"240","62","425","#6a4c93","fill-opacity:0.45",[93,876,879],{"x":877,"y":878,"fill":97},"673","77","17",[93,881,884],{"x":882,"y":883,"fill":97},"112","124","page moved or deleted",[81,886],{"x":870,"y":882,"width":887,"height":750,"fill":801,"style":874},"100",[93,889,892],{"x":890,"y":891,"fill":97},"348","127","4",[93,894,897],{"x":895,"y":896,"fill":97},"121","174","typo in a written path",[81,899],{"x":870,"y":900,"width":751,"height":750,"fill":107,"style":874},"162",[93,902,905],{"x":903,"y":904,"fill":97},"298","177","2",[93,907,909],{"x":95,"y":908,"fill":123,"style":822},"214","2,100-page documentation site, first 30 days with the required check",[161,911,912],{},"Without fragment checking, three quarters of these would have shipped unnoticed.",[14,914,915,916,919],{},"For headings that are linked from outside the site — support articles, blog posts, other products' docs — keep the old anchor working. Many generators support explicit heading IDs (",[175,917,918],{},"## System requirements {#install-requirements}","), which lets you change the wording without changing the ID.",[27,921,923],{"id":922},"renamed-and-deleted-pages","Renamed and Deleted Pages",[14,925,926,927,931,932,25],{},"When a pull request moves or deletes a page, the internal check lists every page that still links to the old path. Fix the links in the same pull request, and add a redirect for the old URL so external links and bookmarks keep working. See ",[21,928,930],{"href":929},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages\u002F","Configuring Redirects on Cloudflare Pages"," or ",[21,933,935],{"href":934},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-redirects-and-rewrites-for-static-sites\u002F","Netlify Redirects and Rewrites for Static Sites",[14,937,938,939,941],{},"A small extra check helps: compare the list of HTML files in the pull request build with the list from ",[175,940,724],{},", and fail if a page disappears without a matching redirect rule. It catches the case where nothing internal links to the page any more, but search engines and readers still do.",[27,943,945],{"id":944},"measured-impact","Measured Impact",[14,947,948],{},"A 2,100-page developer documentation site added the internal lychee check as a required status. In its first month it blocked 23 pull requests, 17 of them for anchors broken by heading edits. The scheduled production crawl, which had been finding 30–50 new broken internal links per month, found two in the following quarter, both from content edited directly in production outside the normal workflow. The external job flagged about 1.5% of external links as dead in the first nightly run, which the team cleared over two weeks.",[27,950,952],{"id":951},"pitfalls-rollback","Pitfalls & Rollback",[32,954,955,961,970,976,982,992],{},[35,956,957,960],{},[702,958,959],{},"Checking the live domain."," Absolute links to your own site are checked against production, where new pages do not exist yet; exclude or remap the domain.",[35,962,963,966,967,969],{},[702,964,965],{},"Base paths."," Sites served from a subpath need ",[175,968,254],{}," pointed at the right folder, or every root-relative link fails.",[35,971,972,975],{},[702,973,974],{},"Generated 404 pages."," They often contain deliberately relative links; exclude them.",[35,977,978,981],{},[702,979,980],{},"Blocking on external links."," The check becomes flaky and people learn to ignore or bypass it.",[35,983,984,987,988,991],{},[702,985,986],{},"Links inside code samples."," Example URLs in code blocks are not meant to resolve; exclude placeholder domains such as ",[175,989,990],{},"example.com"," paths used only in samples.",[35,993,994,997],{},[702,995,996],{},"Rollback:"," make the check non-required; it can still report without blocking.",[27,999,1001],{"id":1000},"conclusion","Conclusion",[14,1003,1004],{},"Checking links on every pull request stops broken links before they reach readers. Build the site, run lychee offline against the output with fragment checking as a required, fast and deterministic check, and handle external links in a separate cached, non-blocking job plus a nightly run. Fix links and add redirects in the same pull request that moves a page, and scheduled crawls of production become a safety net rather than the main defence.",[27,1006,1008],{"id":1007},"faq","FAQ",[1010,1011,1013],"h3",{"id":1012},"should-i-check-links-in-the-markdown-source-or-the-built-html","Should I check links in the Markdown source or the built HTML?",[14,1015,1016],{},"The built HTML. Generators rewrite links, add base paths, generate navigation and create anchors from headings, so the output is what readers actually follow. Checking the build catches broken navigation and anchor links that a source check cannot see.",[1010,1018,1020],{"id":1019},"which-link-checker-should-i-use-in-ci","Which link checker should I use in CI?",[14,1022,1023],{},"lychee is a fast, widely used choice. It is a single binary written in Rust, checks local files and remote URLs concurrently, supports fragment checking, caching and exclusions, and has an official GitHub Action.",[1010,1025,1027],{"id":1026},"how-do-i-stop-external-links-from-making-ci-flaky","How do I stop external links from making CI flaky?",[14,1029,1030],{},"Check internal links on every pull request as a blocking check, and check external links separately - either non-blocking on pull requests or on a nightly schedule with retries and a cache. External sites rate-limit, time out and block bots, none of which your pull request caused.",[1010,1032,1034],{"id":1033},"can-a-link-checker-verify-anchor-links-to-headings","Can a link checker verify anchor links to headings?",[14,1036,1037],{},"Yes. lychee's include-fragments option checks that the id in a link such as \u002Fdocs\u002Finstall\u002F#requirements exists on the target page. Heading renames are one of the most common sources of broken links in documentation.",[27,1039,1041],{"id":1040},"related","Related",[32,1043,1044,1053,1060,1065,1072],{},[35,1045,1046,1049,1050,1052],{},[702,1047,1048],{},"Parent:"," ",[21,1051,24],{"href":23}," — the full docs workflow.",[35,1054,1055,1059],{},[21,1056,1058],{"href":1057},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci\u002F","Editorial Checks with Vale in CI"," — the prose check that runs alongside.",[35,1061,1062,1064],{},[21,1063,51],{"href":50}," — the production safety net.",[35,1066,1067,1071],{},[21,1068,1070],{"href":1069},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge\u002F","Logging 404s at the Edge"," — finding broken inbound links.",[35,1073,1074,1078],{},[21,1075,1077],{"href":1076},"\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"," — other checks on the built site.",[1080,1081,1082],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .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 pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":185,"searchDepth":198,"depth":198,"links":1084},[1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1101],{"id":29,"depth":198,"text":30},{"id":54,"depth":198,"text":55},{"id":169,"depth":198,"text":170},{"id":317,"depth":198,"text":318},{"id":691,"depth":198,"text":692},{"id":829,"depth":198,"text":830},{"id":922,"depth":198,"text":923},{"id":944,"depth":198,"text":945},{"id":951,"depth":198,"text":952},{"id":1000,"depth":198,"text":1001},{"id":1007,"depth":198,"text":1008,"children":1096},[1097,1098,1099,1100],{"id":1012,"depth":204,"text":1013},{"id":1019,"depth":204,"text":1020},{"id":1026,"depth":204,"text":1027},{"id":1033,"depth":204,"text":1034},{"id":1040,"depth":198,"text":1041},[1103,1106,1109,1110],{"name":1104,"item":1105},"Home","\u002F",{"name":1107,"item":1108},"Production-Ready Deployment & CI\u002FCD Workflows","\u002Fproduction-ready-deployment-cicd-workflows\u002F",{"name":24,"item":23},{"name":5,"item":1111},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests\u002F","2026-09-18","Catch broken internal links and anchors before merge by checking the built site in CI with lychee, and handle external links without flaky failures.","md",[1116,1117,1118,1119],{"q":1013,"a":1016},{"q":1020,"a":1023},{"q":1027,"a":1030},{"q":1034,"a":1037},{},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests",{"title":5,"description":1113},"production-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests\u002Findex","article","dCiAs3yQUTHpqln1j9SMJ8Tedr-jild0N9VPMGY-POM",[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,1468,1471,1474,1477,1480,1481,1484,1486,1488,1491,1494,1497,1500,1503,1506,1509,1511,1514,1517,1520,1523,1526,1529,1532,1535,1538,1541,1544,1546,1549,1551,1554,1557,1560,1563,1565,1568,1571,1574,1577,1580,1583,1586,1589,1592,1595,1598,1601,1604,1606,1609,1612,1615,1618,1621,1624,1627,1630,1633,1636,1639,1642,1645,1648,1651,1654,1657,1660,1663],{"path":1128,"title":1129},"\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":1131,"title":1132},"\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":1134,"title":1135},"\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":1137,"title":1138},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1140,"title":1141},"\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":1143,"title":1144},"\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":1146,"title":1147},"\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":1149,"title":1150},"\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":1152,"title":1153},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress","Docs Frameworks: Docusaurus, Starlight and VitePress",{"path":1155,"title":1156},"\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":1158,"title":1159},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight","Migrating from MkDocs to Starlight",{"path":1161,"title":1162},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fversioned-documentation-with-docusaurus","Versioned Documentation with Docusaurus",{"path":1164,"title":1165},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fvitepress-for-library-documentation","VitePress for Library Documentation",{"path":1167,"title":1168},"\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":1170,"title":1171},"\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":1173,"title":1174},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1176,"title":1177},"\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":1179,"title":1180},"\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":1182,"title":1183},"\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":1185,"title":1186},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1188,"title":1189},"\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":1191,"title":1192},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1194,"title":1195},"\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":1197,"title":1198},"\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":1200,"title":1201},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fspeeding-up-slow-jekyll-builds","Speeding Up Slow Jekyll Builds",{"path":1203,"title":1204},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fupgrading-jekyll-and-ruby-versions-safely","Upgrading Jekyll and Ruby Versions Safely",{"path":1206,"title":1207},"\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":1209,"title":1210},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1212,"title":1213},"\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":1215,"title":1216},"\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":1218,"title":1219},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-gatsby-to-astro","Migrating from Gatsby to Astro",{"path":1221,"title":1222},"\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":1224,"title":1225},"\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":1227,"title":1228},"\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":1230,"title":1231},"\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":1233,"title":1234},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1236,"title":1237},"\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":1239,"title":1240},"\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":1242,"title":1243},"\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":1245,"title":1246},"\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":1248,"title":1249},"\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":1251,"title":1252},"\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":1254,"title":1255},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites","Search for Static Sites",{"path":1257,"title":1258},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Findexing-hugo-sites-with-pagefind","Indexing Hugo Sites with Pagefind",{"path":1260,"title":1261},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fmultilingual-search-on-static-sites","Multilingual Search on Static Sites",{"path":1263,"title":1264},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fpagefind-vs-algolia-docsearch","Pagefind vs Algolia DocSearch",{"path":1266,"title":1267},"\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":1269,"title":1270},"\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":1272,"title":1273},"\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":1275,"title":1276},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fevaluating-ssg-accessibility-defaults","Evaluating SSG Accessibility Defaults",{"path":1278,"title":1279},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1281,"title":1282},"\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":1284,"title":1285},"\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":1287,"title":1288},"\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":1290,"title":1291},"\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":1293,"title":1294},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1296,"title":1297},"\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":1299,"title":1300},"\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":1302,"title":1303},"\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":1305,"title":1306},"\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":1308,"title":1309},"\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":1311,"title":1312},"\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":1314,"title":1315},"\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":1317,"title":1318},"\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":1320,"title":1321},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1323,"title":1324},"\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":1326,"title":1327},"\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":1329,"title":1330},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Ffont-display-optional-vs-swap","font-display: optional vs swap",{"path":1332,"title":1333},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1335,"title":1336},"\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":1338,"title":1339},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fpreloading-fonts-without-double-downloads","Preloading Fonts Without Double Downloads",{"path":1341,"title":1342},"\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":1344,"title":1345},"\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":1347,"title":1348},"\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":1350,"title":1351},"\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":1353,"title":1354},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1356,"title":1357},"\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":1359,"title":1360},"\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":1362,"title":1363},"\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":1365,"title":1366},"\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":1368,"title":1369},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1371,"title":1372},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1374,"title":1375},"\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":1377,"title":1378},"\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":1380,"title":1381},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1383,"title":1384},"\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":1386,"title":1387},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Freplacing-react-islands-with-web-components","Replacing React Islands with Web Components",{"path":1389,"title":1390},"\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":1392,"title":1393},"\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":1395,"title":1396},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1398,"title":1399},"\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":1401,"title":1402},"\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":1404,"title":1405},"\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":1407,"title":1408},"\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":1410,"title":1411},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci","Performance Budgets and Lighthouse CI",{"path":1413,"title":1414},"\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":1416,"title":1417},"\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":1419,"title":1420},"\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":1422,"title":1423},"\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":1425,"title":1426},"\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":1428,"title":1429},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fauditing-unused-preloads","Auditing Unused Preloads",{"path":1431,"title":1432},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed","Resource Hints and Navigation Speed",{"path":1434,"title":1435},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Finstant-navigation-with-speculation-rules","Instant Navigation with Speculation Rules",{"path":1437,"title":1438},"\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":1440,"title":1441},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fprefetching-links-in-astro","Prefetching Links in Astro",{"path":1443,"title":1444},"\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":1446,"title":1447},"\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":1449,"title":1450},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1452,"title":1453},"\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":1455,"title":1456},"\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":1458,"title":1459},"\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":1461,"title":1462},"\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":1464,"title":1465},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1467,"title":930},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages",{"path":1469,"title":1470},"\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":1472,"title":1473},"\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":1475,"title":1476},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1478,"title":1479},"\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":1121,"title":5},{"path":1482,"title":1483},"\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":1485,"title":1058},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci",{"path":1487,"title":24},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams",{"path":1489,"title":1490},"\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":1492,"title":1493},"\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":1495,"title":1496},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fbuilding-astro-sites-with-github-actions","Building Astro Sites with GitHub Actions",{"path":1498,"title":1499},"\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":1501,"title":1502},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-github-pages-with-actions","Deploying to GitHub Pages with Actions",{"path":1504,"title":1505},"\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":1507,"title":1508},"\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":1510,"title":41},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds",{"path":1512,"title":1513},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fmatrix-builds-for-multi-site-monorepos","Matrix Builds for Multi-Site Monorepos",{"path":1515,"title":1516},"\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":1518,"title":1519},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1521,"title":1522},"\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":1524,"title":1525},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1527,"title":1528},"\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":1530,"title":1531},"\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":1533,"title":1534},"\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":1536,"title":1537},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1539,"title":1540},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops","Alerting on Cache Hit Ratio Drops",{"path":1542,"title":1543},"\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":1545,"title":51},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fcrawling-for-broken-links-on-a-schedule",{"path":1547,"title":1548},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production","Monitoring Static Sites in Production",{"path":1550,"title":1070},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge",{"path":1552,"title":1553},"\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":1555,"title":1556},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fconfiguring-vercel-for-hugo-and-eleventy","Configuring Vercel for Hugo and Eleventy",{"path":1558,"title":1559},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1561,"title":1562},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1564,"title":935},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-redirects-and-rewrites-for-static-sites",{"path":1566,"title":1567},"\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":1569,"title":1570},"\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":1572,"title":1573},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":1575,"title":1576},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1578,"title":1579},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1581,"title":1582},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments","Password-Protecting Preview Deployments",{"path":1584,"title":1585},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts","Previewing Headless CMS Drafts",{"path":1587,"title":1588},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys","Visual Regression Testing on Preview Deploys",{"path":1590,"title":1591},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1593,"title":1594},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fcanary-releases-for-static-sites","Canary Releases for Static Sites",{"path":1596,"title":1597},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Ffeature-flags-on-static-sites","Feature Flags on Static Sites",{"path":1599,"title":1600},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1602,"title":1603},"\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":1605,"title":1077},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url",{"path":1607,"title":1608},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fauditing-npm-dependencies-in-ssg-pipelines","Auditing npm Dependencies in SSG Pipelines",{"path":1610,"title":1611},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fenabling-hsts-and-preload-safely","Enabling HSTS and Preload Safely",{"path":1613,"title":1614},"\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":1616,"title":1617},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites","Security Headers and Hardening for Static Sites",{"path":1619,"title":1620},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc","Securing Deploy Credentials with GitHub OIDC",{"path":1622,"title":1623},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsubresource-integrity-for-third-party-assets","Subresource Integrity for Third-Party Assets",{"path":1625,"title":1626},"\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":1628,"title":1629},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3","Clean URLs and Trailing Slashes on S3",{"path":1631,"title":1632},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects","CloudFront Functions for Redirects",{"path":1634,"title":1635},"\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":1637,"title":1638},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites","Self-Hosting Static Sites on S3, Nginx and Caddy",{"path":1640,"title":1641},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy","Serving a Static Site with Caddy",{"path":1643,"title":1644},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx","Serving a Static Site with Nginx",{"path":1646,"title":1647},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps","Zero-Downtime Deploys with Symlink Swaps",{"path":1649,"title":1650},"\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":1652,"title":1653},"\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":1655,"title":1656},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites","Serverless Functions for Static Sites",{"path":1658,"title":1659},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers","Netlify Functions vs Cloudflare Workers",{"path":1661,"title":1662},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication","Protecting a Static Site Behind Authentication",{"path":1664,"title":1665},"\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",1789722847254]