[{"data":1,"prerenderedAt":985},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages":977},{"id":4,"title":5,"body":6,"description":956,"extension":957,"meta":958,"navigation":970,"path":971,"seo":972,"stem":975,"__hash__":976},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002Findex.md","Configuring stale-if-error for origin outages",{"type":7,"value":8,"toc":944},"minimark",[9,13,28,55,187,192,195,280,284,301,310,329,349,361,365,368,373,376,448,455,459,462,527,536,539,543,546,613,619,623,639,761,764,768,775,876,893,897,929,934,937,940],[10,11,5],"h1",{"id":12},"configuring-stale-if-error-for-origin-outages",[14,15,16,17,22,23,27],"p",{},"This scenario sits under the ",[18,19,21],"a",{"href":20},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002F","CDN edge caching configuration"," guide inside ",[18,24,26],{"href":25},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",", and addresses one failure mode: your origin returns 5xx or times out, and the edge dutifully forwards the error to users instead of falling back to the perfectly good copy it cached minutes ago.",[14,29,30,31,35,36,40,41,44,45,49,50,54],{},"The fix is the RFC 5861 ",[32,33,34],"code",{},"stale-if-error"," directive, usually paired with the ",[18,37,39],{"href":38},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002F","stale-while-revalidate directive"," that handles the normal expiry path. Both ride on the same ",[32,42,43],{},"Cache-Control"," response header, so the ",[18,46,48],{"href":47},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002F","directive precedence rules from the HTTP caching headers guide"," apply here too. Configured correctly, an origin outage becomes invisible: the edge keeps serving stale HTML and JSON with a fast TTFB (≤ 200ms) while the origin recovers, and your ",[18,51,53],{"href":52},"\u002Fcore-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools\u002F","Largest Contentful Paint"," stays under 2.5s instead of collapsing into an error page. The danger is the opposite failure — serving stale content forever, or serving it when you should not — so the configuration has to be precise.",[14,56,57],{},[58,59,66,67,66,71,66,75,66,85,66,92,66,101,66,107,66,112,66,119,66,123,66,128,66,132,66,138,66,142,66,145,66,148,66,154,66,158,66,164,66,167,66,172,66,177,66,182,66],"svg",{"xmlns":60,"viewBox":61,"width":62,"role":63,"ariaLabel":64,"style":65},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 300","100%","img","Decision flow for how the edge responds when the origin returns an error, using stale-if-error","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:#001d3d"," ",[68,69,70],"title",{},"stale-if-error decision flow",[72,73,74],"desc",{},"How the edge chooses between serving a stale cached copy and forwarding the origin error.",[76,77],"rect",{"x":78,"y":78,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"1","758","298","10","none","currentColor","stroke-opacity:0.18",[86,87,91],"text",{"x":88,"y":89,"fill":83,"style":90},"24","36","font-size:17px;font-weight:700","Edge behaviour during an origin error",[76,93],{"x":94,"y":95,"width":96,"height":97,"rx":98,"fill":99,"stroke":99,"style":100},"44","64","150","56","6","#0466c8","fill-opacity:0.14",[86,102,106],{"x":103,"y":104,"fill":83,"style":105},"119","88","font-size:12px;font-weight:600;text-anchor:middle","Origin returns",[86,108,111],{"x":103,"y":109,"fill":83,"style":110},"106","font-size:12px;text-anchor:middle","5xx \u002F timeout",[113,114],"line",{"x1":115,"y1":116,"x2":117,"y2":116,"stroke":83,"style":118},"194","92","244","stroke-opacity:0.5",[76,120],{"x":117,"y":95,"width":121,"height":97,"rx":98,"fill":83,"stroke":83,"style":122},"170","stroke-opacity:0.4;fill-opacity:0.06",[86,124,127],{"x":125,"y":126,"fill":83,"style":105},"329","86","Stale copy exists",[86,129,131],{"x":125,"y":130,"fill":83,"style":110},"104","within s-i-e window?",[76,133],{"x":134,"y":95,"width":117,"height":97,"rx":98,"fill":135,"stroke":136,"style":137},"470","#ffc300","#b8860b","fill-opacity:0.22",[86,139,141],{"x":140,"y":126,"fill":83,"style":105},"592","YES: serve STALE 200",[86,143,144],{"x":140,"y":130,"fill":83,"style":110},"TTFB held under 200ms",[113,146],{"x1":147,"y1":116,"x2":134,"y2":116,"stroke":83,"style":118},"414",[86,149,153],{"x":150,"y":151,"fill":83,"style":152},"442","84","font-size:11px;text-anchor:middle","yes",[113,155],{"x1":125,"y1":156,"x2":125,"y2":157,"stroke":83,"style":118},"120","160",[86,159,163],{"x":160,"y":161,"fill":83,"style":162},"345","142","font-size:11px","no",[76,165],{"x":117,"y":157,"width":117,"height":166,"rx":98,"fill":83,"stroke":83,"style":122},"50",[86,168,171],{"x":169,"y":170,"fill":83,"style":105},"366","190","Forward error: 5xx",[113,173],{"x1":88,"y1":174,"x2":175,"y2":174,"stroke":83,"style":176},"238","736","stroke-opacity:0.3",[86,178,181],{"x":88,"y":179,"fill":83,"style":180},"262","font-size:12px","Size the window to p95 recovery time; pair with origin-side alarms.",[86,183,186],{"x":88,"y":184,"fill":83,"style":185},"284","font-size:12px;fill-opacity:0.8","Cap the window to seconds for prices, stock, and auth-sensitive routes.",[188,189,191],"h2",{"id":190},"rapid-diagnosis","Rapid diagnosis",[14,193,194],{},"Before changing headers, confirm the edge is actually forwarding origin errors rather than absorbing them.",[196,197,198,220,253,267,274],"ul",{},[199,200,201,202,205,206,209,210,209,213,216,217,219],"li",{},"Take the origin down or point a request at a known-failing route, then ",[32,203,204],{},"curl -I"," the edge URL. A ",[32,207,208],{},"502","\u002F",[32,211,212],{},"503",[32,214,215],{},"504"," reaching the client means no ",[32,218,34],{}," fallback is active.",[199,221,222,223,226,227,226,230,233,234,237,238,226,241,244,245,248,249,252],{},"Inspect the cache-status header (",[32,224,225],{},"CF-Cache-Status",", ",[32,228,229],{},"X-Cache",[32,231,232],{},"Fastly"," debug headers, ",[32,235,236],{},"x-vercel-cache","). During the outage you want to see a hit\u002Fstale indicator (",[32,239,240],{},"STALE",[32,242,243],{},"HIT","), not ",[32,246,247],{},"MISS"," or ",[32,250,251],{},"ERROR",".",[199,254,255,256,258,259,263,264,266],{},"Check the ",[32,257,43],{}," on the ",[260,261,262],"em",{},"origin"," response in the browser Network tab. If ",[32,265,34],{}," is absent, the edge has nothing authorising a fallback.",[199,268,269,270,273],{},"Confirm the resource was cached at all before the outage — a route with ",[32,271,272],{},"Cache-Control: no-store"," or a cookie-fragmented cache key has no stale copy to fall back to.",[199,275,276,277,279],{},"Look at the edge TTL: if the object already expired past the ",[32,278,34],{}," window, the edge correctly stops serving it.",[188,281,283],{"id":282},"root-cause-analysis","Root cause analysis",[14,285,286,290,291,294,295,297,298,300],{},[287,288,289],"strong",{},"1. The directive is simply missing."," The most common cause: origin responses carry ",[32,292,293],{},"max-age"," but no ",[32,296,34],{},". The edge has a fresh-or-revalidate model only, so on a ",[32,299,215],{}," it has no instruction to reuse the expired object and passes the error through.",[14,302,303,66,306,309],{},[287,304,305],{},"2. The grace window is too short.",[32,307,308],{},"stale-if-error=60"," only covers a one-minute blip. A real deploy failure or database incident runs for minutes to hours; once the window lapses, the edge resumes forwarding errors. The window must be sized to your realistic mean-time-to-recovery, not to a hypothetical.",[14,311,312,66,315,226,318,321,322,209,325,328],{},[287,313,314],{},"3. The route was never cacheable.",[32,316,317],{},"Set-Cookie",[32,319,320],{},"Vary: Cookie",", or ",[32,323,324],{},"Cache-Control: private",[32,326,327],{},"no-store"," on the HTML means the edge never stored a shared copy, so there is nothing to serve stale. Personalised pages are the classic blind spot here.",[14,330,331,334,335,337,338,341,342,344,345,348],{},[287,332,333],{},"4. The CDN does not honour the standard directive."," Several CDNs ignore the HTTP ",[32,336,34],{}," directive and require their own configuration knob (Cloudflare's ",[260,339,340],{},"Always Online"," \u002F Tiered Cache serve-stale, Fastly's VCL ",[32,343,34],{}," in ",[32,346,347],{},"vcl_fetch",", Akamai's downstream caching settings). Setting the header alone does nothing on those platforms.",[14,350,351,354,355,357,358,360],{},[287,352,353],{},"5. The error window is masking a real incident."," A subtler failure mode is success that hides a problem. Once ",[32,356,34],{}," is serving day-old content, edge status codes look healthy even though the origin has been down for an hour. Teams that alarm on edge 5xx rate will see nothing fire while customers quietly view stale data. The directive is doing its job, but observability has to move to the origin tier — otherwise the safety net becomes a blindfold. This is not a reason to avoid ",[32,359,34],{},"; it is a reason to pair it with origin-side error-rate and saturation alarms so the outage is still visible to you even when it is invisible to users.",[188,362,364],{"id":363},"step-by-step-resolution","Step-by-step resolution",[14,366,367],{},"Apply these in order of impact; each lists the expected outcome.",[369,370,372],"h3",{"id":371},"_1-add-stale-if-error-to-origin-responses","1. Add stale-if-error to origin responses",[14,374,375],{},"Emit both grace directives on cacheable HTML and API payloads from the origin:",[377,378,383],"pre",{"className":379,"code":380,"language":381,"meta":382,"style":382},"language-nginx shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","# Origin (or shielding tier) response for cacheable HTML.\nlocation \u002F {\n  add_header Cache-Control \"public, max-age=60, stale-while-revalidate=600, stale-if-error=86400\";\n  # trade-off: an 86400s (24h) error window can serve day-old content during a\n  # long outage. Do NOT use this length for prices, stock, or auth-sensitive\n  # pages — cap stale-if-error to seconds there and prefer an explicit error.\n}\n","nginx","",[32,384,385,393,408,424,430,436,442],{"__ignoreMap":382},[386,387,389],"span",{"class":113,"line":388},1,[386,390,392],{"class":391},"sIIH1","# Origin (or shielding tier) response for cacheable HTML.\n",[386,394,396,400,404],{"class":113,"line":395},2,[386,397,399],{"class":398},"sP5qI","location",[386,401,403],{"class":402},"seIZK"," \u002F ",[386,405,407],{"class":406},"syybb","{\n",[386,409,411,414,417,421],{"class":113,"line":410},3,[386,412,413],{"class":398},"  add_header ",[386,415,416],{"class":406},"Cache-Control ",[386,418,420],{"class":419},"s-_DF","\"public, max-age=60, stale-while-revalidate=600, stale-if-error=86400\"",[386,422,423],{"class":406},";\n",[386,425,427],{"class":113,"line":426},4,[386,428,429],{"class":391},"  # trade-off: an 86400s (24h) error window can serve day-old content during a\n",[386,431,433],{"class":113,"line":432},5,[386,434,435],{"class":391},"  # long outage. Do NOT use this length for prices, stock, or auth-sensitive\n",[386,437,439],{"class":113,"line":438},6,[386,440,441],{"class":391},"  # pages — cap stale-if-error to seconds there and prefer an explicit error.\n",[386,443,445],{"class":113,"line":444},7,[386,446,447],{"class":406},"}\n",[14,449,450,451,454],{},"Expected outcome: during a ",[32,452,453],{},"5xx","\u002Ftimeout, the edge serves the last good copy for up to 24h instead of an error page, holding TTFB at edge-cache speed (≤ 50ms) rather than an origin round-trip plus failure.",[369,456,458],{"id":457},"_2-enable-serve-stale-on-cdns-that-ignore-the-header","2. Enable serve-stale on CDNs that ignore the header",[14,460,461],{},"On Fastly, make the behaviour explicit in VCL so a fetch failure reuses stale:",[377,463,467],{"className":464,"code":465,"language":466,"meta":382,"style":382},"language-vcl shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","sub vcl_fetch {\n  # Reuse cached object for 24h if origin errors, refresh quietly for 10m.\n  set beresp.stale_if_error = 86400s;\n  set beresp.stale_while_revalidate = 600s;\n  if (beresp.status >= 500 && stale.exists) {\n    return (deliver_stale);\n  }\n  # trade-off: deliver_stale masks origin 5xx from monitoring. Keep origin\n  # error rate alarms on the origin tier, not on edge status codes, or a\n  # multi-hour outage will look healthy from the edge.\n}\n","vcl",[32,468,469,474,479,484,489,494,499,504,510,516,522],{"__ignoreMap":382},[386,470,471],{"class":113,"line":388},[386,472,473],{},"sub vcl_fetch {\n",[386,475,476],{"class":113,"line":395},[386,477,478],{},"  # Reuse cached object for 24h if origin errors, refresh quietly for 10m.\n",[386,480,481],{"class":113,"line":410},[386,482,483],{},"  set beresp.stale_if_error = 86400s;\n",[386,485,486],{"class":113,"line":426},[386,487,488],{},"  set beresp.stale_while_revalidate = 600s;\n",[386,490,491],{"class":113,"line":432},[386,492,493],{},"  if (beresp.status >= 500 && stale.exists) {\n",[386,495,496],{"class":113,"line":438},[386,497,498],{},"    return (deliver_stale);\n",[386,500,501],{"class":113,"line":444},[386,502,503],{},"  }\n",[386,505,507],{"class":113,"line":506},8,[386,508,509],{},"  # trade-off: deliver_stale masks origin 5xx from monitoring. Keep origin\n",[386,511,513],{"class":113,"line":512},9,[386,514,515],{},"  # error rate alarms on the origin tier, not on edge status codes, or a\n",[386,517,519],{"class":113,"line":518},10,[386,520,521],{},"  # multi-hour outage will look healthy from the edge.\n",[386,523,525],{"class":113,"line":524},11,[386,526,447],{},[14,528,529,530,532,533,535],{},"On Cloudflare, enable ",[260,531,340],{}," and Tiered Cache, or set ",[32,534,43],{}," via a Cache Rule, since the worker\u002Fedge honours serve-stale through those settings rather than the raw directive alone.",[14,537,538],{},"Expected outcome: platforms that drop the standard directive now fall back to stale, eliminating the silent gap where the header was present but inert.",[369,540,542],{"id":541},"_3-make-the-route-cacheable-in-the-first-place","3. Make the route cacheable in the first place",[14,544,545],{},"Strip personalisation from the cache key so a shared stale copy exists:",[377,547,549],{"className":379,"code":548,"language":381,"meta":382,"style":382},"# Don't fragment the HTML cache on analytics\u002Fsession cookies.\nproxy_cache_key \"$scheme$host$request_uri\";\nproxy_ignore_headers Set-Cookie;\nproxy_hide_header Set-Cookie;\n# trade-off: this is only safe for genuinely shared, anonymous HTML.\n# Applying it to logged-in pages leaks one user's cached page to another —\n# segment personalised routes into a separate, non-shared cache.\n",[32,550,551,556,583,591,598,603,608],{"__ignoreMap":382},[386,552,553],{"class":113,"line":388},[386,554,555],{"class":391},"# Don't fragment the HTML cache on analytics\u002Fsession cookies.\n",[386,557,558,561,564,567,570,573,575,578,581],{"class":113,"line":395},[386,559,560],{"class":398},"proxy_cache_key ",[386,562,563],{"class":419},"\"$",[386,565,566],{"class":406},"scheme",[386,568,569],{"class":419},"$",[386,571,572],{"class":406},"host",[386,574,569],{"class":419},[386,576,577],{"class":406},"request_uri",[386,579,580],{"class":419},"\"",[386,582,423],{"class":406},[386,584,585,588],{"class":113,"line":410},[386,586,587],{"class":398},"proxy_ignore_headers ",[386,589,590],{"class":406},"Set-Cookie;\n",[386,592,593,596],{"class":113,"line":426},[386,594,595],{"class":398},"proxy_hide_header ",[386,597,590],{"class":406},[386,599,600],{"class":113,"line":432},[386,601,602],{"class":391},"# trade-off: this is only safe for genuinely shared, anonymous HTML.\n",[386,604,605],{"class":113,"line":438},[386,606,607],{"class":391},"# Applying it to logged-in pages leaks one user's cached page to another —\n",[386,609,610],{"class":113,"line":444},[386,611,612],{"class":391},"# segment personalised routes into a separate, non-shared cache.\n",[14,614,615,616,618],{},"Expected outcome: anonymous HTML now has a shared edge object, giving ",[32,617,34],{}," something to serve and lifting baseline hit ratio above the 85% target for static routes.",[369,620,622],{"id":621},"_4-size-the-window-to-real-recovery-time","4. Size the window to real recovery time",[14,624,625,626,628,629,632,633,635,636,638],{},"Set ",[32,627,34],{}," to comfortably exceed your p95 incident duration (commonly 1-24h for HTML, shorter for data) while keeping ",[32,630,631],{},"stale-while-revalidate"," short (300-600s) so healthy traffic refreshes promptly. The two are independent: ",[32,634,631],{}," handles the normal expiry path, ",[32,637,34],{}," handles the failure path.",[14,640,641],{},[58,642,66,645,66,648,66,651,66,654,66,658,66,663,66,668,66,673,66,680,66,684,66,689,66,692,66,696,66,701,66,707,66,710,66,713,66,742,66,747,66,749,66,753,66,757,66],{"xmlns":60,"viewBox":643,"width":62,"role":63,"ariaLabel":644,"style":65},"0 0 760 352","Timeline of one cached object showing three nested Cache-Control windows measured from the response: a short max-age fresh band, a longer stale-while-revalidate band, and a much longer stale-if-error band that is the only one consulted during an origin error",[68,646,647],{},"Three nested cache windows on one object",[72,649,650],{},"max-age (60s, fresh), stale-while-revalidate (600s, background refresh) and stale-if-error (86400s) all start at the response and nest inside each other; only the stale-if-error window authorises serving during an origin 5xx or timeout.",[76,652],{"x":78,"y":78,"width":79,"height":653,"rx":81,"fill":82,"stroke":83,"style":84},"350",[86,655,657],{"x":88,"y":656,"fill":83,"style":90},"34","Three windows on one object, measured from the response",[113,659],{"x1":96,"y1":660,"x2":96,"y2":661,"stroke":83,"style":662},"60","214","stroke-opacity:0.45;stroke-dasharray:3 3",[86,664,667],{"x":96,"y":665,"fill":83,"style":666},"54","font-size:11px;text-anchor:middle;fill-opacity:0.75","response cached (t=0)",[86,669,672],{"x":96,"y":670,"fill":83,"style":671},"74","font-size:12px;font-weight:600","max-age 60s — FRESH, served without contacting origin",[76,674],{"x":96,"y":675,"width":676,"height":677,"rx":678,"fill":99,"stroke":99,"style":679},"80","95","22","4","fill-opacity:0.16",[86,681,683],{"x":96,"y":682,"fill":83,"style":671},"122","stale-while-revalidate 600s — serve stale, refresh in background",[76,685],{"x":96,"y":686,"width":687,"height":677,"rx":678,"fill":83,"stroke":83,"style":688},"128","255","stroke-opacity:0.4;fill-opacity:0.07",[86,690,691],{"x":96,"y":121,"fill":83,"style":671},"stale-if-error 86400s (24h) — failure-only fallback",[76,693],{"x":96,"y":694,"width":695,"height":677,"rx":678,"fill":135,"stroke":136,"style":137},"176","530",[697,698],"path",{"d":699,"fill":82,"stroke":83,"style":700},"M446 174 l-8 26 M456 174 l-8 26","stroke-opacity:0.55",[86,702,706],{"x":703,"y":704,"fill":83,"style":705},"474","191","font-size:11px;font-weight:600","consulted ONLY on origin 5xx \u002F timeout",[113,708],{"x1":96,"y1":661,"x2":709,"y2":661,"stroke":83,"style":118},"680",[697,711],{"d":712,"fill":82,"stroke":83,"style":700},"M446 210 l-6 8 M456 210 l-6 8",[714,715,66,717,66,720,66,725,66,728,66,731,66,734,66,737,66,739,66],"g",{"fill":83,"style":716},"font-size:11px;fill-opacity:0.8",[113,718],{"x1":96,"y1":661,"x2":96,"y2":719,"stroke":83,"style":118},"221",[86,721,724],{"x":96,"y":722,"style":723},"234","text-anchor:middle","0s",[113,726],{"x1":727,"y1":661,"x2":727,"y2":719,"stroke":83,"style":118},"245",[86,729,730],{"x":727,"y":722,"style":723},"60s",[113,732],{"x1":733,"y1":661,"x2":733,"y2":719,"stroke":83,"style":118},"405",[86,735,736],{"x":733,"y":722,"style":723},"600s",[113,738],{"x1":709,"y1":661,"x2":709,"y2":719,"stroke":83,"style":118},[86,740,741],{"x":709,"y":722,"style":723},"86400s",[86,743,746],{"x":744,"y":722,"fill":83,"style":745},"512","font-size:10px;fill-opacity:0.55","axis broken — not to scale",[113,748],{"x1":88,"y1":179,"x2":175,"y2":179,"stroke":83,"style":176},[86,750,752],{"x":88,"y":751,"fill":83,"style":180},"286","Only the stale-if-error window authorises serving during an outage; max-age and s-w-r cover the healthy path only.",[86,754,756],{"x":88,"y":755,"fill":83,"style":185},"308","The windows nest: each longer band contains the shorter, and all are measured from the same response time.",[86,758,760],{"x":88,"y":759,"fill":83,"style":185},"330","Size stale-if-error to p95 recovery; keep stale-while-revalidate short so healthy traffic refreshes promptly.",[14,762,763],{},"Expected outcome: routine expiries stay fresh within ~10 minutes, while a multi-hour outage is still fully absorbed.",[188,765,767],{"id":766},"verification","Verification",[14,769,770,771,774],{},"Confirm the fallback works ",[260,772,773],{},"and"," that it heals after recovery:",[776,777,778,828,843,855,870],"ol",{},[199,779,780,783,784,786,787],{},[287,781,782],{},"Before\u002Fafter header diff."," Compare the origin ",[32,785,43],{},":\n",[377,788,792],{"className":789,"code":790,"language":791,"meta":382,"style":382},"language-bash shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","curl -sI https:\u002F\u002Fyourdomain.com\u002F | grep -i cache-control\n# before: cache-control: public, max-age=60\n# after:  cache-control: public, max-age=60, stale-while-revalidate=600, stale-if-error=86400\n","bash",[32,793,794,818,823],{"__ignoreMap":382},[386,795,796,799,803,806,809,812,815],{"class":113,"line":388},[386,797,798],{"class":402},"curl",[386,800,802],{"class":801},"sf6mN"," -sI",[386,804,805],{"class":419}," https:\u002F\u002Fyourdomain.com\u002F",[386,807,808],{"class":398}," |",[386,810,811],{"class":402}," grep",[386,813,814],{"class":801}," -i",[386,816,817],{"class":419}," cache-control\n",[386,819,820],{"class":113,"line":395},[386,821,822],{"class":391},"# before: cache-control: public, max-age=60\n",[386,824,825],{"class":113,"line":410},[386,826,827],{"class":391},"# after:  cache-control: public, max-age=60, stale-while-revalidate=600, stale-if-error=86400\n",[199,829,830,833,834,837,838,840,841,252],{},[287,831,832],{},"Simulate the outage."," Block the origin (firewall rule or kill the upstream) and re-request the edge. Expected: ",[32,835,836],{},"200 OK"," from cache with a ",[32,839,240],{}," cache-status, not a ",[32,842,453],{},[199,844,845,848,849,851,852,854],{},[287,846,847],{},"Confirm recovery."," Restore the origin. Within the ",[32,850,631],{}," window the edge should serve stale once more, then transparently refresh to a ",[32,853,243],{}," with the new body — verify the response content updates.",[199,856,857,860,861,863,864,866,867,869],{},[287,858,859],{},"CI assertion."," Add a synthetic check that fails the pipeline if the production ",[32,862,43],{}," for ",[32,865,209],{}," lacks ",[32,868,34],{},", so a future origin refactor cannot silently drop it.",[199,871,872,875],{},[287,873,874],{},"RUM field check."," During the next real origin blip, watch error-page rate and TTFB p75 in RUM. Expected: client-visible 5xx rate near zero and TTFB held under 200ms, even while origin error rate spikes.",[14,877,878,879,882,883,887,888,892],{},"This pattern composes with the broader edge configuration and with deliberate purging — when you ",[260,880,881],{},"do"," need to evict the stale copy after the origin is fixed, follow ",[18,884,886],{"href":885},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Fpurging-cdn-cache-by-tag-on-deploy\u002F","purging CDN cache by tag on deploy"," so the recovered origin and the edge agree on a single fresh copy. Where the browser itself must survive an offline origin, a ",[18,889,891],{"href":890},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002F","Service Worker caching strategy"," provides a second, client-side stale layer beneath the edge.",[188,894,896],{"id":895},"related","Related",[196,898,899,905,911,917,923],{},[199,900,901,904],{},[18,902,903],{"href":20},"Up one level: CDN edge caching configuration"," — the parent guide covering TTL tiers and edge rule orchestration.",[199,906,907,910],{},[18,908,909],{"href":38},"Stale-while-revalidate implementation"," — the companion directive for the normal expiry path, and how it differs from the failure path here.",[199,912,913,916],{},[18,914,915],{"href":47},"HTTP Cache-Control headers explained"," — directive precedence and syntax behind these headers.",[199,918,919,922],{},[18,920,921],{"href":885},"Purging CDN cache by tag on deploy"," — how to evict the stale object once the origin is healthy again.",[199,924,925,928],{},[18,926,927],{"href":890},"Service Worker caching strategies"," — a client-side resilience layer that complements edge serve-stale.",[930,931,933],"script",{"type":932},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"Article\",\n  \"headline\": \"Configuring stale-if-error for origin outages\",\n  \"description\": \"Serve stale content during origin outages using stale-if-error and stale-while-revalidate Cache-Control directives at the CDN edge, with diagnosis, fixes, and verification.\",\n  \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002F\",\n  \"datePublished\": \"2026-06-18\",\n  \"dateModified\": \"2026-07-18\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\", \"logo\": { \"@type\": \"ImageObject\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Flogo.svg\" } },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002F\" }\n}\n",[930,935,936],{"type":932},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Serve stale content during origin outages with stale-if-error\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"name\": \"Add stale-if-error\", \"text\": \"Emit stale-while-revalidate and stale-if-error on cacheable origin responses.\" },\n    { \"@type\": \"HowToStep\", \"name\": \"Enable serve-stale\", \"text\": \"Configure serve-stale on CDNs that ignore the raw directive, e.g. Fastly VCL or Cloudflare Always Online.\" },\n    { \"@type\": \"HowToStep\", \"name\": \"Make route cacheable\", \"text\": \"Strip personalisation from the cache key so a shared stale copy exists.\" },\n    { \"@type\": \"HowToStep\", \"name\": \"Size the window\", \"text\": \"Set stale-if-error to exceed p95 incident duration while keeping stale-while-revalidate short.\" },\n    { \"@type\": \"HowToStep\", \"name\": \"Verify and monitor\", \"text\": \"Simulate an outage, confirm a STALE 200, confirm recovery, and add a CI header assertion.\" }\n  ]\n}\n",[930,938,939],{"type":932},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Advanced Caching Strategies & CDN Architecture\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"CDN Edge Caching Configuration\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 4, \"name\": \"Configuring stale-if-error for origin outages\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002F\" }\n  ]\n}\n",[941,942,943],"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 .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sIIH1, html code.shiki .sIIH1{--shiki-default:#66707B;--shiki-dark:#66707B;--shiki-light:#66707B}html pre.shiki code .sP5qI, html code.shiki .sP5qI{--shiki-default:#A0111F;--shiki-dark:#A0111F;--shiki-light:#A0111F}html pre.shiki code .seIZK, html code.shiki .seIZK{--shiki-default:#702C00;--shiki-dark:#702C00;--shiki-light:#702C00}html pre.shiki code .syybb, html code.shiki .syybb{--shiki-default:#0E1116;--shiki-dark:#0E1116;--shiki-light:#0E1116}html pre.shiki code .s-_DF, html code.shiki .s-_DF{--shiki-default:#032563;--shiki-dark:#032563;--shiki-light:#032563}html pre.shiki code .sf6mN, html code.shiki .sf6mN{--shiki-default:#023B95;--shiki-dark:#023B95;--shiki-light:#023B95}",{"title":382,"searchDepth":395,"depth":395,"links":945},[946,947,948,954,955],{"id":190,"depth":395,"text":191},{"id":282,"depth":395,"text":283},{"id":363,"depth":395,"text":364,"children":949},[950,951,952,953],{"id":371,"depth":410,"text":372},{"id":457,"depth":410,"text":458},{"id":541,"depth":410,"text":542},{"id":621,"depth":410,"text":622},{"id":766,"depth":395,"text":767},{"id":895,"depth":395,"text":896},"Keep serving cached pages from the edge when the origin returns 5xx or times out, using stale-if-error and stale-while-revalidate.","md",{"slug":12,"type":959,"breadcrumb":960,"datePublished":968,"dateModified":969},"article",[961,963,964,966],{"name":962,"url":209},"Home",{"name":26,"url":25},{"name":965,"url":20},"CDN Edge Caching Configuration",{"name":5,"url":967},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002F","2026-06-18","2026-07-18",true,"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages",{"title":973,"description":974},"Configuring stale-if-error for Origin Outages","Serve stale content during origin outages with stale-if-error and stale-while-revalidate Cache-Control at the CDN edge: diagnosis, fixes, verification.","advanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002Findex","X4-4HG5WJ3jv_JWtLVRFarKacegM7LWEsIv-5kp-q2Y",[978,981],{"title":965,"path":979,"stem":980,"children":-1},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration","advanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Findex",{"title":982,"path":983,"stem":984,"children":-1},"HTTP Cache-Control Headers Explained","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained","advanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002Findex",1784398534181]