[{"data":1,"prerenderedAt":1755},["ShallowReactive",2],{"page:\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge":3,"all-docs-nav":1215},{"id":4,"title":5,"body":6,"breadcrumb":1191,"dateModified":1200,"datePublished":1200,"description":1201,"extension":1202,"faq":1203,"meta":1208,"navigation":1209,"path":1210,"seo":1211,"slug":12,"stem":1212,"type":1213,"__hash__":1214},"content\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge\u002Findex.md","Logging 404s at the Edge",{"type":7,"value":8,"toc":1172},"minimark",[9,13,17,26,31,53,57,60,471,474,478,491,558,651,655,658,738,741,844,847,851,859,935,939,967,971,974,978,981,1051,1055,1087,1091,1094,1098,1103,1106,1110,1113,1117,1120,1124,1127,1131,1168],[10,11,5],"h1",{"id":12},"logging-404s-at-the-edge",[14,15,16],"p",{},"Every static site has a list of URLs that readers try and fail to reach: pages moved during a restructure without a redirect, links in old blog posts on other sites, bookmarks from a previous generator's URL scheme, typos in a popular forum answer. Those 404s are invisible to the build, rarely reported by readers, and — because they come from outside — impossible to find by crawling your own site. The one place they are all recorded is the CDN's request log.",[14,18,19,20,25],{},"This guide collects 404s from the edge on Cloudflare and CloudFront, filters out bot noise, ranks paths by reader impact and turns the top of the list into redirects on a weekly cadence. It is part of ",[21,22,24],"a",{"href":23},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002F","Monitoring Static Sites in Production",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,38,50],"ul",{},[35,36,37],"li",{},"Access to the CDN's request logs or analytics: Cloudflare Logpush or Workers Analytics Engine, CloudFront standard logs to S3, or an equivalent.",[35,39,40,41,45,46,25],{},"A redirect mechanism for the host — see ",[21,42,44],{"href":43},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages\u002F","Configuring Redirects on Cloudflare Pages"," or ",[21,47,49],{"href":48},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects\u002F","CloudFront Functions for Redirects",[35,51,52],{},"A place to run a weekly query: Athena, BigQuery, ClickHouse, or the CDN's own GraphQL API.",[27,54,56],{"id":55},"step-1-capture-404s-with-their-context","Step 1: Capture 404s With Their Context",[14,58,59],{},"A 404 count per path is not enough to act on; you need to know where readers came from. Record path, referrer, user agent class and country for every 404. On Cloudflare, a small Worker in front of static assets can write that to Workers Analytics Engine without any log pipeline:",[61,62,67],"pre",{"className":63,"code":64,"language":65,"meta":66,"style":66},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F worker.js — wraps static assets, records 404s\nexport default {\n  async fetch(request, env, ctx) {\n    const res = await env.ASSETS.fetch(request);\n    if (res.status === 404) {\n      const url = new URL(request.url);\n      const ua = request.headers.get('user-agent') ?? '';\n      ctx.waitUntil(Promise.resolve(env.NOTFOUND.writeDataPoint({\n        blobs: [url.pathname, request.headers.get('referer') ?? '', uaClass(ua), request.cf?.country ?? ''],\n        indexes: [url.pathname.slice(0, 96)],\n      })));\n    }\n    return res;\n  },\n};\nconst uaClass = (ua) => \u002Fbot|crawl|spider|scan|curl|python|go-http\u002Fi.test(ua) ? 'bot'\n  : \u002FMozilla\\\u002F5\\.0\u002F.test(ua) ? 'browser' : 'other';\n","js","",[68,69,70,79,93,124,154,171,191,225,258,292,314,320,326,335,341,347,427],"code",{"__ignoreMap":66},[71,72,75],"span",{"class":73,"line":74},"line",1,[71,76,78],{"class":77},"sJ8bj","\u002F\u002F worker.js — wraps static assets, records 404s\n",[71,80,82,86,89],{"class":73,"line":81},2,[71,83,85],{"class":84},"szBVR","export",[71,87,88],{"class":84}," default",[71,90,92],{"class":91},"sVt8B"," {\n",[71,94,96,99,103,106,110,113,116,118,121],{"class":73,"line":95},3,[71,97,98],{"class":84},"  async",[71,100,102],{"class":101},"sScJk"," fetch",[71,104,105],{"class":91},"(",[71,107,109],{"class":108},"s4XuR","request",[71,111,112],{"class":91},", ",[71,114,115],{"class":108},"env",[71,117,112],{"class":91},[71,119,120],{"class":108},"ctx",[71,122,123],{"class":91},") {\n",[71,125,127,130,134,137,140,143,146,148,151],{"class":73,"line":126},4,[71,128,129],{"class":84},"    const",[71,131,133],{"class":132},"sj4cs"," res",[71,135,136],{"class":84}," =",[71,138,139],{"class":84}," await",[71,141,142],{"class":91}," env.",[71,144,145],{"class":132},"ASSETS",[71,147,25],{"class":91},[71,149,150],{"class":101},"fetch",[71,152,153],{"class":91},"(request);\n",[71,155,157,160,163,166,169],{"class":73,"line":156},5,[71,158,159],{"class":84},"    if",[71,161,162],{"class":91}," (res.status ",[71,164,165],{"class":84},"===",[71,167,168],{"class":132}," 404",[71,170,123],{"class":91},[71,172,174,177,180,182,185,188],{"class":73,"line":173},6,[71,175,176],{"class":84},"      const",[71,178,179],{"class":132}," url",[71,181,136],{"class":84},[71,183,184],{"class":84}," new",[71,186,187],{"class":101}," URL",[71,189,190],{"class":91},"(request.url);\n",[71,192,194,196,199,201,204,207,209,213,216,219,222],{"class":73,"line":193},7,[71,195,176],{"class":84},[71,197,198],{"class":132}," ua",[71,200,136],{"class":84},[71,202,203],{"class":91}," request.headers.",[71,205,206],{"class":101},"get",[71,208,105],{"class":91},[71,210,212],{"class":211},"sZZnC","'user-agent'",[71,214,215],{"class":91},") ",[71,217,218],{"class":84},"??",[71,220,221],{"class":211}," ''",[71,223,224],{"class":91},";\n",[71,226,228,231,234,236,239,241,244,247,250,252,255],{"class":73,"line":227},8,[71,229,230],{"class":91},"      ctx.",[71,232,233],{"class":101},"waitUntil",[71,235,105],{"class":91},[71,237,238],{"class":132},"Promise",[71,240,25],{"class":91},[71,242,243],{"class":101},"resolve",[71,245,246],{"class":91},"(env.",[71,248,249],{"class":132},"NOTFOUND",[71,251,25],{"class":91},[71,253,254],{"class":101},"writeDataPoint",[71,256,257],{"class":91},"({\n",[71,259,261,264,266,268,271,273,275,277,279,282,285,287,289],{"class":73,"line":260},9,[71,262,263],{"class":91},"        blobs: [url.pathname, request.headers.",[71,265,206],{"class":101},[71,267,105],{"class":91},[71,269,270],{"class":211},"'referer'",[71,272,215],{"class":91},[71,274,218],{"class":84},[71,276,221],{"class":211},[71,278,112],{"class":91},[71,280,281],{"class":101},"uaClass",[71,283,284],{"class":91},"(ua), request.cf?.country ",[71,286,218],{"class":84},[71,288,221],{"class":211},[71,290,291],{"class":91},"],\n",[71,293,295,298,301,303,306,308,311],{"class":73,"line":294},10,[71,296,297],{"class":91},"        indexes: [url.pathname.",[71,299,300],{"class":101},"slice",[71,302,105],{"class":91},[71,304,305],{"class":132},"0",[71,307,112],{"class":91},[71,309,310],{"class":132},"96",[71,312,313],{"class":91},")],\n",[71,315,317],{"class":73,"line":316},11,[71,318,319],{"class":91},"      })));\n",[71,321,323],{"class":73,"line":322},12,[71,324,325],{"class":91},"    }\n",[71,327,329,332],{"class":73,"line":328},13,[71,330,331],{"class":84},"    return",[71,333,334],{"class":91}," res;\n",[71,336,338],{"class":73,"line":337},14,[71,339,340],{"class":91},"  },\n",[71,342,344],{"class":73,"line":343},15,[71,345,346],{"class":91},"};\n",[71,348,350,353,356,358,361,364,366,369,372,376,379,382,384,387,389,392,394,397,399,402,404,407,410,413,415,418,421,424],{"class":73,"line":349},16,[71,351,352],{"class":84},"const",[71,354,355],{"class":101}," uaClass",[71,357,136],{"class":84},[71,359,360],{"class":91}," (",[71,362,363],{"class":108},"ua",[71,365,215],{"class":91},[71,367,368],{"class":84},"=>",[71,370,371],{"class":211}," \u002F",[71,373,375],{"class":374},"sA_wV","bot",[71,377,378],{"class":84},"|",[71,380,381],{"class":374},"crawl",[71,383,378],{"class":84},[71,385,386],{"class":374},"spider",[71,388,378],{"class":84},[71,390,391],{"class":374},"scan",[71,393,378],{"class":84},[71,395,396],{"class":374},"curl",[71,398,378],{"class":84},[71,400,401],{"class":374},"python",[71,403,378],{"class":84},[71,405,406],{"class":374},"go-http",[71,408,409],{"class":211},"\u002F",[71,411,412],{"class":84},"i",[71,414,25],{"class":91},[71,416,417],{"class":101},"test",[71,419,420],{"class":91},"(ua) ",[71,422,423],{"class":84},"?",[71,425,426],{"class":211}," 'bot'\n",[71,428,430,433,435,438,442,445,448,450,452,454,456,458,460,463,466,469],{"class":73,"line":429},17,[71,431,432],{"class":84},"  :",[71,434,371],{"class":211},[71,436,437],{"class":374},"Mozilla",[71,439,441],{"class":440},"snhLl","\\\u002F",[71,443,444],{"class":374},"5",[71,446,447],{"class":440},"\\.",[71,449,305],{"class":374},[71,451,409],{"class":211},[71,453,25],{"class":91},[71,455,417],{"class":101},[71,457,420],{"class":91},[71,459,423],{"class":84},[71,461,462],{"class":211}," 'browser'",[71,464,465],{"class":84}," :",[71,467,468],{"class":211}," 'other'",[71,470,224],{"class":91},[14,472,473],{},"On CloudFront, standard logs already contain status, URI, referrer and user agent; point an Athena table at the log bucket and query it directly.",[27,475,477],{"id":476},"step-2-filter-out-the-noise","Step 2: Filter Out the Noise",[14,479,480,481,112,484,112,487,490],{},"Most raw 404s on the public internet are not readers. Vulnerability scanners probe ",[68,482,483],{},"\u002Fwp-login.php",[68,485,486],{},"\u002F.env",[68,488,489],{},"\u002F.git\u002Fconfig"," and thousands of similar paths on every site. On the documentation site measured here, 91% of raw 404 requests were bots. Filter before ranking:",[61,492,496],{"className":493,"code":494,"language":495,"meta":66,"style":66},"language-sql shiki shiki-themes github-light github-dark","-- Athena over CloudFront logs: last 7 days of reader-facing 404s\nSELECT uri, count(*) AS hits,\n       count(DISTINCT referrer) AS referrers,\n       approx_percentile(length(referrer), 0.5) > 1 AS usually_referred\nFROM cloudfront_logs\nWHERE status = 404 AND date >= current_date - interval '7' day\n  AND user_agent LIKE 'Mozilla\u002F5.0%'\n  AND NOT regexp_like(user_agent, '(?i)bot|crawl|spider|scan')\n  AND NOT regexp_like(uri, '(?i)\\.(php|asp|aspx|env|git|bak|sql|zip)$|wp-|\u002Fcgi-bin\u002F|\u002F\\.')\nGROUP BY uri\nORDER BY hits DESC\nLIMIT 50;\n","sql",[68,497,498,503,508,513,518,523,528,533,538,543,548,553],{"__ignoreMap":66},[71,499,500],{"class":73,"line":74},[71,501,502],{},"-- Athena over CloudFront logs: last 7 days of reader-facing 404s\n",[71,504,505],{"class":73,"line":81},[71,506,507],{},"SELECT uri, count(*) AS hits,\n",[71,509,510],{"class":73,"line":95},[71,511,512],{},"       count(DISTINCT referrer) AS referrers,\n",[71,514,515],{"class":73,"line":126},[71,516,517],{},"       approx_percentile(length(referrer), 0.5) > 1 AS usually_referred\n",[71,519,520],{"class":73,"line":156},[71,521,522],{},"FROM cloudfront_logs\n",[71,524,525],{"class":73,"line":173},[71,526,527],{},"WHERE status = 404 AND date >= current_date - interval '7' day\n",[71,529,530],{"class":73,"line":193},[71,531,532],{},"  AND user_agent LIKE 'Mozilla\u002F5.0%'\n",[71,534,535],{"class":73,"line":227},[71,536,537],{},"  AND NOT regexp_like(user_agent, '(?i)bot|crawl|spider|scan')\n",[71,539,540],{"class":73,"line":260},[71,541,542],{},"  AND NOT regexp_like(uri, '(?i)\\.(php|asp|aspx|env|git|bak|sql|zip)$|wp-|\u002Fcgi-bin\u002F|\u002F\\.')\n",[71,544,545],{"class":73,"line":294},[71,546,547],{},"GROUP BY uri\n",[71,549,550],{"class":73,"line":316},[71,551,552],{},"ORDER BY hits DESC\n",[71,554,555],{"class":73,"line":322},[71,556,557],{},"LIMIT 50;\n",[559,560,561,647],"figure",{},[562,563,570,571,570,575,570,579,570,585],"svg",{"viewBox":564,"role":565,"ariaLabelledBy":566,"xmlns":569},"0 0 760 270","img",[567,568],"e404-noise-title","e404-noise-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[572,573,574],"title",{"id":567},"Raw 404s versus reader 404s",[576,577,578],"desc",{"id":568},"A stacked bar of 404 requests in one week. Of 184,000 raw 404s, 167,000 were bots and scanners, 4,500 were browsers requesting probe-like paths, and 12,500 were readers following real links, which is the part worth acting on.",[580,581],"rect",{"x":305,"y":305,"width":582,"height":583,"fill":584},"760","270","#ffffff",[586,587,589,590,589,598,589,607,589,613,589,620,589,626,589,633,589,637,589,643,570],"g",{"style":588},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[591,592,597],"text",{"x":593,"y":594,"fill":595,"style":596},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","One week of 404s, before and after filtering",[580,599],{"x":600,"y":601,"width":602,"height":603,"fill":604,"stroke":605,"style":606},"40","70","620","44","#f8fafc","#556071","stroke-width:1px",[591,608,612],{"x":609,"y":610,"fill":595,"style":611},"350","97","text-anchor:middle","bots and scanners · 167,000 (91%)",[580,614],{"x":615,"y":601,"width":616,"height":603,"fill":617,"opacity":618,"stroke":619,"style":606},"660","17","#ffca3a","0.4","#a97b00",[580,621],{"x":622,"y":601,"width":623,"height":603,"fill":624,"opacity":618,"stroke":625,"style":606},"677","46","#8ac926","#5a8a16",[591,627,632],{"x":628,"y":629,"fill":630,"style":631},"700","138","#3f6410","font-size:11px;font-weight:700;text-anchor:middle","readers",[591,634,636],{"x":628,"y":635,"fill":630,"style":631},"154","12,500",[591,638,642],{"x":602,"y":639,"fill":640,"style":641},"176","#8a6d0f","font-size:11px;text-anchor:middle","probe-like paths from browsers · 4,500",[591,644,646],{"x":593,"y":645,"fill":605,"style":641},"230","Ranking unfiltered 404s puts \u002Fwp-login.php at the top of a site that has never run WordPress",[648,649,650],"figcaption",{},"The actionable part of the 404 log is under a tenth of it; filtering is what makes the report readable.",[27,652,654],{"id":653},"step-3-rank-by-reader-impact-and-decide","Step 3: Rank by Reader Impact and Decide",[14,656,657],{},"For each path in the top fifty, decide one of four outcomes:",[659,660,661,677],"table",{},[662,663,664],"thead",{},[665,666,667,671,674],"tr",{},[668,669,670],"th",{},"Situation",[668,672,673],{},"Action",[668,675,676],{},"Example",[678,679,680,698,714,725],"tbody",{},[665,681,682,686,689],{},[683,684,685],"td",{},"Content moved; clear equivalent exists",[683,687,688],{},"301 redirect",[683,690,691,694,695],{},[68,692,693],{},"\u002Fdocs\u002Finstall"," → ",[68,696,697],{},"\u002Fguides\u002Finstall\u002F",[665,699,700,703,706],{},[683,701,702],{},"Old URL pattern from a previous generator",[683,704,705],{},"pattern redirect",[683,707,708,694,711],{},[68,709,710],{},"\u002Fblog\u002F2021\u002F03\u002Fslug.html",[68,712,713],{},"\u002Fblog\u002Fslug\u002F",[665,715,716,719,722],{},[683,717,718],{},"Content deliberately removed",[683,720,721],{},"410 Gone, or 404 with a helpful page",[683,723,724],{},"retired product docs",[665,726,727,730,733],{},[683,728,729],{},"Typo in an external link, heavy traffic",[683,731,732],{},"redirect, and ask the linking site to fix it",[683,734,735],{},[68,736,737],{},"\u002Fguides\u002Fintsall\u002F",[14,739,740],{},"Referrers tell you which: a 404 whose referrer is your own site is an internal broken link to fix at the source; one whose referrer is a search engine means an indexed URL that needs a redirect; one whose referrer is a single external page means one link somewhere is wrong.",[559,742,743,841],{},[562,744,570,749,570,752,570,755,570,758,570,828],{"viewBox":745,"role":565,"ariaLabelledBy":746,"xmlns":569},"0 0 760 260",[747,748],"e404-ref-title","e404-ref-desc",[572,750,751],{"id":747},"The referrer decides the fix",[576,753,754],{"id":748},"Three referrer types lead to three fixes. A referrer on your own site means an internal broken link: fix the link in the source. A search engine referrer means an indexed old URL: add a redirect. A single external page as referrer means one wrong link elsewhere: add a redirect if traffic is significant and contact the linking site.",[580,756],{"x":305,"y":305,"width":582,"height":757,"fill":584},"260",[586,759,589,760,589,763,589,772,589,778,589,780,589,784,589,786,589,789,589,796,589,800,589,803,589,806,589,809,589,812,570],{"style":588},[591,761,762],{"x":593,"y":594,"fill":595,"style":596},"Where the reader came from tells you what to fix",[580,764],{"x":765,"y":766,"width":767,"height":768,"rx":769,"fill":604,"stroke":770,"style":771},"30","60","200","50","8","#d9e2ef","stroke-width:1.5px",[591,773,777],{"x":774,"y":775,"fill":595,"style":776},"130","90","font-weight:700;text-anchor:middle","your own site",[580,779],{"x":765,"y":774,"width":767,"height":768,"rx":769,"fill":604,"stroke":770,"style":771},[591,781,783],{"x":774,"y":782,"fill":595,"style":776},"160","search engine",[580,785],{"x":765,"y":767,"width":767,"height":768,"rx":769,"fill":604,"stroke":770,"style":771},[591,787,788],{"x":774,"y":645,"fill":595,"style":776},"one external page",[580,790],{"x":791,"y":766,"width":792,"height":768,"rx":769,"fill":793,"opacity":794,"stroke":795,"style":771},"330","400","#ff595e","0.14","#d83b41",[591,797,799],{"x":798,"y":775,"fill":595,"style":611},"530","fix the link in your source; no redirect needed",[580,801],{"x":791,"y":774,"width":792,"height":768,"rx":769,"fill":624,"opacity":802,"stroke":625,"style":771},"0.18",[591,804,805],{"x":798,"y":782,"fill":595,"style":611},"add a 301 to the equivalent page",[580,807],{"x":791,"y":767,"width":792,"height":768,"rx":769,"fill":617,"opacity":808,"stroke":619,"style":771},"0.24",[591,810,811],{"x":798,"y":645,"fill":595,"style":611},"redirect if busy; ask the linking site to update",[586,813,816,817,816,822,816,825,589],{"stroke":605,"fill":814,"style":815},"none","stroke-width:2px","\n      ",[818,819],"path",{"d":820,"style":821},"M232 85 L326 85","marker-end:url(#e404-arrow)",[818,823],{"d":824,"style":821},"M232 155 L326 155",[818,826],{"d":827,"style":821},"M232 225 L326 225",[829,830,589,831,570],"defs",{},[832,833,816,838,589],"marker",{"id":834,"viewBox":835,"refX":769,"refY":444,"markerWidth":836,"markerHeight":836,"orient":837},"e404-arrow","0 0 10 10","7","auto-start-reverse",[818,839],{"d":840,"fill":605},"M0 0 L10 5 L0 10 z",[648,842,843],{},"Same-site referrers are the most valuable find: they are bugs in your own content that every reader of that page can hit.",[14,845,846],{},"On the restructured docs site, 23 of the top fifty 404 paths had same-site referrers — links inside the docs that the restructure had not updated, mostly in pages written in raw HTML where the link checker's Markdown parser had not looked. Fixing those at the source removed the 404 and the need for a redirect at the same time. Another 19 came from search engines and got redirects. The remaining eight each came from one external page, usually a forum answer or a partner's documentation; for the five with meaningful traffic, a redirect plus a short note to the linking site's maintainer fixed it permanently.",[27,848,850],{"id":849},"step-4-make-it-a-weekly-loop","Step 4: Make It a Weekly Loop",[14,852,853,854,858],{},"Run the query weekly, post the top twenty to the team channel with a suggested action for each, and add redirects in a pull request the same week. Redirect targets are verified by the build, as described in ",[21,855,857],{"href":856},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration\u002F","Keeping Redirects Working After an SSG Migration",". Add an alert for spikes: a path that goes from zero to more than 100 reader 404s in an hour almost always means a deploy removed or renamed something popular.",[559,860,861,932],{},[562,862,570,866,570,869,570,872,570,874],{"viewBox":564,"role":565,"ariaLabelledBy":863,"xmlns":569},[864,865],"e404-trend-title","e404-trend-desc",[572,867,868],{"id":864},"Reader 404s per day over eight weeks of weekly fixes",[576,870,871],{"id":865},"A falling line of reader 404s per day. It starts at about 1,800 per day in week one after a site restructure, drops to about 700 after the first batch of 15 redirects, to about 300 after week three, and levels off around 180 per day from week five onward.",[580,873],{"x":305,"y":305,"width":582,"height":583,"fill":584},[586,875,589,876,589,879,589,883,589,885,589,890,589,894,589,898,589,904,589,909,589,915,589,920,589,924,589,928,570],{"style":588},[591,877,878],{"x":593,"y":594,"fill":595,"style":596},"Reader 404s per day, weekly redirect batches",[73,880],{"x1":601,"y1":881,"x2":882,"y2":881,"stroke":605,"style":771},"220","720",[73,884],{"x1":601,"y1":768,"x2":601,"y2":881,"stroke":605,"style":771},[591,886,305],{"x":887,"y":888,"fill":605,"style":889},"62","224","font-size:11px;text-anchor:end",[591,891,893],{"x":887,"y":892,"fill":605,"style":889},"136","1,000",[591,895,897],{"x":887,"y":896,"fill":605,"style":889},"54","2,000",[899,900],"polyline",{"points":901,"fill":814,"stroke":902,"style":903},"90,62 170,64 250,158 330,194 410,200 490,204 570,205 650,204 710,205","#6a4c93","stroke-width:2.5px",[591,905,908],{"x":310,"y":906,"fill":902,"style":907},"56","font-size:11px;font-weight:700","~1,800 \u002F day",[591,910,914],{"x":911,"y":912,"fill":595,"style":913},"256","150","font-size:11px","15 redirects",[591,916,919],{"x":917,"y":918,"fill":595,"style":913},"336","186","+22",[591,921,923],{"x":602,"y":922,"fill":630,"style":907},"196","~180 \u002F day",[591,925,927],{"x":775,"y":926,"fill":605,"style":641},"240","wk 1",[591,929,931],{"x":930,"y":926,"fill":605,"style":641},"710","wk 8",[648,933,934],{},"Two weekly batches of redirects removed most of the reader-facing 404s; the remainder is a long tail of one-off typos.",[27,936,938],{"id":937},"other-status-codes-worth-watching","Other Status Codes Worth Watching",[14,940,941,942,946,947,45,950,953,954,958,959,962,963,966],{},"The same log pipeline answers adjacent questions cheaply. Count ",[943,944,945],"strong",{},"410 and 404 responses on asset paths"," separately from pages: a spike in missing ",[68,948,949],{},".js",[68,951,952],{},".css"," files usually follows a deploy that removed old hashed assets while cached HTML still referenced them, which is covered in ",[21,955,957],{"href":956},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads\u002F","Atomic Deploys vs Incremental Uploads",". Count ",[943,960,961],{},"301 responses by path",": a redirect that serves thousands of hits a week points at an internal link or sitemap entry that should be updated to the final URL, saving every reader a round trip. And watch ",[943,964,965],{},"5xx responses",", which on a static host should be essentially zero; any at all usually mean an edge function throwing or an origin misconfiguration on self-hosted setups.",[27,968,970],{"id":969},"make-the-404-page-useful","Make the 404 Page Useful",[14,972,973],{},"Some 404s will always remain, so the page readers land on matters. A static 404 page can still help: include a search box, links to the main sections, and — because it is generated at build time — a short list of pages whose paths share words with common mistyped URLs. A few lines of client-side script can suggest the closest existing path by comparing the requested URL with the sitemap. On this site, adding a search box and \"did you mean\" suggestion to the 404 page meant 41% of readers who hit a 404 clicked through to a real page, against 12% before. Make sure the page returns a real 404 status — hosts serve custom 404 pages correctly by default, but single-page-app fallback settings can turn them into soft 200s that search engines index.",[27,975,977],{"id":976},"measured-impact","Measured Impact",[14,979,980],{},"Eight weeks after a documentation restructure moved 340 pages:",[659,982,983,996],{},[662,984,985],{},[665,986,987,990,993],{},[668,988,989],{},"Measure",[668,991,992],{},"Week 1",[668,994,995],{},"Week 8",[678,997,998,1009,1019,1029,1040],{},[665,999,1000,1003,1006],{},[683,1001,1002],{},"Reader 404s per day",[683,1004,1005],{},"~1,800",[683,1007,1008],{},"~180",[665,1010,1011,1014,1016],{},[683,1012,1013],{},"Redirects added from the log",[683,1015,305],{},[683,1017,1018],{},"61",[665,1020,1021,1024,1027],{},[683,1022,1023],{},"Internal broken links found via same-site referrers",[683,1025,1026],{},"23",[683,1028,305],{},[665,1030,1031,1034,1037],{},[683,1032,1033],{},"Search Console \"Not found (404)\" URLs",[683,1035,1036],{},"290",[683,1038,1039],{},"34",[665,1041,1042,1045,1048],{},[683,1043,1044],{},"404-page click-through to a real page",[683,1046,1047],{},"12%",[683,1049,1050],{},"41%",[27,1052,1054],{"id":1053},"pitfalls-rollback","Pitfalls & Rollback",[32,1056,1057,1063,1069,1075,1081],{},[35,1058,1059,1062],{},[943,1060,1061],{},"Ranking unfiltered logs."," Bot probes dominate raw 404s. Filter by user agent and path pattern before looking.",[35,1064,1065,1068],{},[943,1066,1067],{},"Redirecting everything to the homepage."," Search engines treat that as a soft 404, and readers lose context. Redirect to an equivalent page or return 404.",[35,1070,1071,1074],{},[943,1072,1073],{},"Keeping raw IPs indefinitely."," They are personal data in many jurisdictions; truncate or drop them.",[35,1076,1077,1080],{},[943,1078,1079],{},"A 404 page that returns 200."," Check the status explicitly; SPA fallbacks often break it.",[35,1082,1083,1086],{},[943,1084,1085],{},"Rollback:"," the logging Worker or Athena table is independent of the site. Removing it stops collection without affecting pages.",[27,1088,1090],{"id":1089},"conclusion","Conclusion",[14,1092,1093],{},"Edge logs are the only complete record of the URLs readers fail to reach. Capturing each 404 with its referrer and user agent, filtering the 91% that are bots, and acting weekly on the top of the ranked list cut reader-facing 404s on a restructured docs site by 90% in eight weeks, with 61 redirects. A 404 page with search and suggestions turned many of the rest into successful visits.",[27,1095,1097],{"id":1096},"faq","FAQ",[1099,1100,1102],"h3",{"id":1101},"why-log-404s-at-the-edge-instead-of-in-analytics","Why log 404s at the edge instead of in analytics?",[14,1104,1105],{},"Analytics scripts run in the browser and often do not load on error pages, and many readers block them. The CDN sees every request, including those from links in other sites, feed readers and bots, so its logs are the complete record.",[1099,1107,1109],{"id":1108},"how-do-i-tell-bot-404s-from-reader-404s","How do I tell bot 404s from reader 404s?",[14,1111,1112],{},"Look at the referrer and user agent. Reader 404s usually have a referrer from a search engine, another site or your own pages, and a browser user agent. Bot probes for paths like \u002Fwp-admin or \u002F.env have no referrer and scanner user agents. Filter them out before ranking.",[1099,1114,1116],{"id":1115},"should-every-404-get-a-redirect","Should every 404 get a redirect?",[14,1118,1119],{},"No. Redirect paths that readers actually reach and that have a clear equivalent page. Leave probes, typos with no pattern and paths for content that was deliberately removed as 404 or 410.",[1099,1121,1123],{"id":1122},"how-long-should-404-logs-be-kept","How long should 404 logs be kept?",[14,1125,1126],{},"Thirty to ninety days is enough to see patterns and seasonal traffic. Aggregate counts per path can be kept longer; drop raw IP addresses or truncate them to respect privacy.",[27,1128,1130],{"id":1129},"related","Related",[32,1132,1133,1142,1149,1154,1161],{},[35,1134,1135,1138,1139,1141],{},[943,1136,1137],{},"Parent:"," ",[21,1140,24],{"href":23}," — the full monitoring set.",[35,1143,1144,1148],{},[21,1145,1147],{"href":1146},"\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"," — broken links going out; this page covers links coming in.",[35,1150,1151,1153],{},[21,1152,857],{"href":856}," — preventing the restructure spike.",[35,1155,1156,1160],{},[21,1157,1159],{"href":1158},"\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"," — applying the fixes on Netlify.",[35,1162,1163,1167],{},[21,1164,1166],{"href":1165},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops\u002F","Alerting on Cache Hit Ratio Drops"," — another signal from the same logs.",[1169,1170,1171],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .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 pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}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":66,"searchDepth":81,"depth":81,"links":1173},[1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1190],{"id":29,"depth":81,"text":30},{"id":55,"depth":81,"text":56},{"id":476,"depth":81,"text":477},{"id":653,"depth":81,"text":654},{"id":849,"depth":81,"text":850},{"id":937,"depth":81,"text":938},{"id":969,"depth":81,"text":970},{"id":976,"depth":81,"text":977},{"id":1053,"depth":81,"text":1054},{"id":1089,"depth":81,"text":1090},{"id":1096,"depth":81,"text":1097,"children":1185},[1186,1187,1188,1189],{"id":1101,"depth":95,"text":1102},{"id":1108,"depth":95,"text":1109},{"id":1115,"depth":95,"text":1116},{"id":1122,"depth":95,"text":1123},{"id":1129,"depth":81,"text":1130},[1192,1194,1197,1198],{"name":1193,"item":409},"Home",{"name":1195,"item":1196},"Production-Ready Deployment & CI\u002FCD Workflows","\u002Fproduction-ready-deployment-cicd-workflows\u002F",{"name":24,"item":23},{"name":5,"item":1199},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge\u002F","2026-09-18","Collect every 404 a static site serves at the CDN edge, rank paths by reader impact, separate bots from people, and turn the top of the list into redirects.","md",[1204,1205,1206,1207],{"q":1102,"a":1105},{"q":1109,"a":1112},{"q":1116,"a":1119},{"q":1123,"a":1126},{},true,"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge",{"title":5,"description":1201},"production-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge\u002Findex","article","UP4nUn4nKbNwJFqz0lKjl8ylcPxxmjLAZoX10-VX0x8",[1216,1219,1222,1225,1228,1231,1234,1237,1240,1243,1246,1249,1252,1255,1258,1261,1264,1267,1270,1273,1276,1279,1282,1285,1288,1291,1294,1297,1300,1302,1305,1308,1311,1314,1317,1320,1323,1326,1329,1332,1335,1338,1341,1344,1347,1350,1353,1356,1359,1362,1365,1368,1371,1374,1377,1380,1383,1386,1389,1392,1395,1398,1401,1404,1407,1410,1413,1416,1419,1422,1425,1428,1431,1434,1437,1440,1443,1446,1449,1452,1455,1458,1461,1464,1467,1470,1473,1476,1479,1482,1485,1488,1491,1494,1497,1500,1503,1506,1509,1512,1515,1518,1521,1524,1527,1530,1533,1536,1539,1542,1545,1548,1551,1554,1556,1559,1562,1565,1568,1571,1574,1577,1580,1583,1586,1589,1592,1595,1598,1601,1604,1607,1610,1613,1616,1619,1622,1625,1628,1631,1633,1636,1638,1640,1641,1644,1647,1650,1653,1655,1658,1661,1664,1667,1670,1673,1676,1679,1681,1684,1687,1690,1693,1696,1699,1702,1705,1708,1711,1714,1717,1720,1722,1725,1728,1731,1734,1737,1740,1743,1746,1749,1752],{"path":1217,"title":1218},"\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":1220,"title":1221},"\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":1223,"title":1224},"\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":1226,"title":1227},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1229,"title":1230},"\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":1232,"title":1233},"\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":1235,"title":1236},"\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":1238,"title":1239},"\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":1241,"title":1242},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress","Docs Frameworks: Docusaurus, Starlight and VitePress",{"path":1244,"title":1245},"\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":1247,"title":1248},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight","Migrating from MkDocs to Starlight",{"path":1250,"title":1251},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fversioned-documentation-with-docusaurus","Versioned Documentation with Docusaurus",{"path":1253,"title":1254},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fvitepress-for-library-documentation","VitePress for Library Documentation",{"path":1256,"title":1257},"\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":1259,"title":1260},"\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":1262,"title":1263},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1265,"title":1266},"\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":1268,"title":1269},"\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":1271,"title":1272},"\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":1274,"title":1275},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1277,"title":1278},"\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":1280,"title":1281},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1283,"title":1284},"\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":1286,"title":1287},"\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":1289,"title":1290},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fspeeding-up-slow-jekyll-builds","Speeding Up Slow Jekyll Builds",{"path":1292,"title":1293},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fupgrading-jekyll-and-ruby-versions-safely","Upgrading Jekyll and Ruby Versions Safely",{"path":1295,"title":1296},"\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":1298,"title":1299},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1301,"title":857},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration",{"path":1303,"title":1304},"\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":1306,"title":1307},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-gatsby-to-astro","Migrating from Gatsby to Astro",{"path":1309,"title":1310},"\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":1312,"title":1313},"\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":1315,"title":1316},"\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":1318,"title":1319},"\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":1321,"title":1322},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1324,"title":1325},"\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":1327,"title":1328},"\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":1330,"title":1331},"\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":1333,"title":1334},"\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":1336,"title":1337},"\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":1339,"title":1340},"\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":1342,"title":1343},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites","Search for Static Sites",{"path":1345,"title":1346},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Findexing-hugo-sites-with-pagefind","Indexing Hugo Sites with Pagefind",{"path":1348,"title":1349},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fmultilingual-search-on-static-sites","Multilingual Search on Static Sites",{"path":1351,"title":1352},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fpagefind-vs-algolia-docsearch","Pagefind vs Algolia DocSearch",{"path":1354,"title":1355},"\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":1357,"title":1358},"\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":1360,"title":1361},"\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":1363,"title":1364},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fevaluating-ssg-accessibility-defaults","Evaluating SSG Accessibility Defaults",{"path":1366,"title":1367},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1369,"title":1370},"\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":1372,"title":1373},"\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":1375,"title":1376},"\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":1378,"title":1379},"\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":1381,"title":1382},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1384,"title":1385},"\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":1387,"title":1388},"\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":1390,"title":1391},"\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":1393,"title":1394},"\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":1396,"title":1397},"\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":1399,"title":1400},"\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":1402,"title":1403},"\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":1405,"title":1406},"\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":1408,"title":1409},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1411,"title":1412},"\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":1414,"title":1415},"\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":1417,"title":1418},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Ffont-display-optional-vs-swap","font-display: optional vs swap",{"path":1420,"title":1421},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1423,"title":1424},"\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":1426,"title":1427},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fpreloading-fonts-without-double-downloads","Preloading Fonts Without Double Downloads",{"path":1429,"title":1430},"\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":1432,"title":1433},"\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":1435,"title":1436},"\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":1438,"title":1439},"\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":1441,"title":1442},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1444,"title":1445},"\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":1447,"title":1448},"\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":1450,"title":1451},"\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":1453,"title":1454},"\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":1456,"title":1457},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1459,"title":1460},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1462,"title":1463},"\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":1465,"title":1466},"\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":1468,"title":1469},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1471,"title":1472},"\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":1474,"title":1475},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Freplacing-react-islands-with-web-components","Replacing React Islands with Web Components",{"path":1477,"title":1478},"\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":1480,"title":1481},"\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":1483,"title":1484},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1486,"title":1487},"\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":1489,"title":1490},"\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":1492,"title":1493},"\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":1495,"title":1496},"\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":1498,"title":1499},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci","Performance Budgets and Lighthouse CI",{"path":1501,"title":1502},"\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":1504,"title":1505},"\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":1507,"title":1508},"\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":1510,"title":1511},"\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":1513,"title":1514},"\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":1516,"title":1517},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fauditing-unused-preloads","Auditing Unused Preloads",{"path":1519,"title":1520},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed","Resource Hints and Navigation Speed",{"path":1522,"title":1523},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Finstant-navigation-with-speculation-rules","Instant Navigation with Speculation Rules",{"path":1525,"title":1526},"\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":1528,"title":1529},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fprefetching-links-in-astro","Prefetching Links in Astro",{"path":1531,"title":1532},"\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":1534,"title":1535},"\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":1537,"title":1538},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1540,"title":1541},"\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":1543,"title":1544},"\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":1546,"title":1547},"\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":1549,"title":1550},"\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":1552,"title":1553},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1555,"title":44},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages",{"path":1557,"title":1558},"\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":1560,"title":1561},"\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":1563,"title":1564},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1566,"title":1567},"\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":1569,"title":1570},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests","Checking Links in Pull Requests",{"path":1572,"title":1573},"\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":1575,"title":1576},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci","Editorial Checks with Vale in CI",{"path":1578,"title":1579},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1581,"title":1582},"\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":1584,"title":1585},"\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":1587,"title":1588},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fbuilding-astro-sites-with-github-actions","Building Astro Sites with GitHub Actions",{"path":1590,"title":1591},"\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":1593,"title":1594},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-github-pages-with-actions","Deploying to GitHub Pages with Actions",{"path":1596,"title":1597},"\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":1599,"title":1600},"\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":1602,"title":1603},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1605,"title":1606},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fmatrix-builds-for-multi-site-monorepos","Matrix Builds for Multi-Site Monorepos",{"path":1608,"title":1609},"\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":1611,"title":1612},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1614,"title":1615},"\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":1617,"title":1618},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1620,"title":1621},"\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":1623,"title":1624},"\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":1626,"title":1627},"\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":1629,"title":1630},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1632,"title":1166},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops",{"path":1634,"title":1635},"\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":1637,"title":1147},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fcrawling-for-broken-links-on-a-schedule",{"path":1639,"title":24},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production",{"path":1210,"title":5},{"path":1642,"title":1643},"\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":1645,"title":1646},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fconfiguring-vercel-for-hugo-and-eleventy","Configuring Vercel for Hugo and Eleventy",{"path":1648,"title":1649},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1651,"title":1652},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1654,"title":1159},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-redirects-and-rewrites-for-static-sites",{"path":1656,"title":1657},"\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":1659,"title":1660},"\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":1662,"title":1663},"\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":1665,"title":1666},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1668,"title":1669},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1671,"title":1672},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments","Password-Protecting Preview Deployments",{"path":1674,"title":1675},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts","Previewing Headless CMS Drafts",{"path":1677,"title":1678},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys","Visual Regression Testing on Preview Deploys",{"path":1680,"title":957},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads",{"path":1682,"title":1683},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fcanary-releases-for-static-sites","Canary Releases for Static Sites",{"path":1685,"title":1686},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Ffeature-flags-on-static-sites","Feature Flags on Static Sites",{"path":1688,"title":1689},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1691,"title":1692},"\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":1694,"title":1695},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",{"path":1697,"title":1698},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fauditing-npm-dependencies-in-ssg-pipelines","Auditing npm Dependencies in SSG Pipelines",{"path":1700,"title":1701},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fenabling-hsts-and-preload-safely","Enabling HSTS and Preload Safely",{"path":1703,"title":1704},"\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":1706,"title":1707},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites","Security Headers and Hardening for Static Sites",{"path":1709,"title":1710},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc","Securing Deploy Credentials with GitHub OIDC",{"path":1712,"title":1713},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsubresource-integrity-for-third-party-assets","Subresource Integrity for Third-Party Assets",{"path":1715,"title":1716},"\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":1718,"title":1719},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3","Clean URLs and Trailing Slashes on S3",{"path":1721,"title":49},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects",{"path":1723,"title":1724},"\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":1726,"title":1727},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites","Self-Hosting Static Sites on S3, Nginx and Caddy",{"path":1729,"title":1730},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy","Serving a Static Site with Caddy",{"path":1732,"title":1733},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx","Serving a Static Site with Nginx",{"path":1735,"title":1736},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps","Zero-Downtime Deploys with Symlink Swaps",{"path":1738,"title":1739},"\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":1741,"title":1742},"\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":1744,"title":1745},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites","Serverless Functions for Static Sites",{"path":1747,"title":1748},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers","Netlify Functions vs Cloudflare Workers",{"path":1750,"title":1751},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication","Protecting a Static Site Behind Authentication",{"path":1753,"title":1754},"\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",1789722847826]