[{"data":1,"prerenderedAt":805},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis":797},{"id":4,"title":5,"body":6,"description":777,"extension":778,"meta":779,"navigation":790,"path":791,"seo":792,"stem":795,"__hash__":796},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis\u002Findex.md","Choosing SWR Windows for HTML and APIs",{"type":7,"value":8,"toc":760},"minimark",[9,14,46,49,176,181,210,214,220,226,232,238,375,379,384,391,395,401,447,451,454,458,469,475,536,540,553,557,572,576,590,594,620,624,636,642,648,654,658,670,679,688,700,709,718,722,745,750,753,756],[10,11,13],"h1",{"id":12},"how-to-choose-stale-while-revalidate-windows-for-html-and-apis","How to Choose stale-while-revalidate Windows for HTML and APIs",[15,16,17,18,23,24,28,29,33,34,37,38,41,42,45],"p",{},"This guide answers the tuning question behind ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002F","Stale-While-Revalidate Implementation",", part of ",[19,25,27],{"href":26},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",". The ",[30,31,32],"code",{},"stale-while-revalidate=N"," directive lets a cache serve a response for up to N seconds after it expires, while it fetches a fresh copy in the background. ",[30,35,36],{},"stale-if-error=N"," lets it serve a stale copy for up to N seconds when the origin is failing. Together with ",[30,39,40],{},"max-age","\u002F",[30,43,44],{},"s-maxage"," they define three windows — fresh, stale-but-usable, and emergency — and their sizes determine both performance and how old content can get.",[15,47,48],{},"There is no universal value. The right windows depend on how often the content changes, how much staleness is acceptable to users and the business, how often each cached object is requested, and whether you can purge on change. A news homepage, a product price API, a documentation page and a configuration endpoint each need different numbers.",[15,50,51],{},[52,53,59,60,59,67,59,71,59,74,59,83,59,89,59,94,59,103,59,110,59,117,59,121,59,126,59,130,59,137,59,143,59,147,59,151,59,155,59,159,59,163,59,167,59,172,59],"svg",{"viewBox":54,"width":55,"role":56,"ariaLabel":57,"style":58},"0 0 760 184","100%","img","Timeline showing the fresh window, the stale-while-revalidate window and the stale-if-error window after a response is cached.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[61,62],"rect",{"className":63,"x":65,"y":65,"width":55,"height":55,"fill":66},[64],"svg-canvas","0","#ffffff",[68,69,70],"title",{},"The three caching windows for one response",[72,73,57],"desc",{},[61,75],{"x":76,"y":76,"width":77,"height":78,"rx":79,"fill":80,"stroke":81,"style":82},"1","758","182","10","none","currentColor","stroke-opacity:0.18",[84,85,70],"text",{"x":86,"y":87,"fill":81,"style":88},"28.0","34.0","font-size:16px;font-weight:700",[84,90,93],{"x":86,"y":91,"fill":81,"style":92},"74.0","font-size:12.5px;font-weight:700","Windows",[61,95],{"x":96,"y":97,"width":98,"height":99,"rx":100,"fill":101,"stroke":101,"style":102},"101.9","56.0","67.9","26.0","3","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[84,104,109],{"x":105,"y":106,"fill":81,"style":107,"textAnchor":108},"135.9","73.5","font-size:11.5px","middle","fresh",[61,111],{"x":112,"y":97,"width":113,"height":99,"rx":100,"fill":114,"stroke":115,"style":116},"171.4","415.0","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[84,118,120],{"x":119,"y":106,"fill":81,"style":107,"textAnchor":108},"378.9","stale-while-revalidate",[61,122],{"x":123,"y":97,"width":124,"height":99,"rx":100,"fill":81,"stroke":81,"style":125},"587.9","137.3","fill-opacity:0.06;stroke-opacity:0.4",[84,127,129],{"x":128,"y":106,"fill":81,"style":107,"textAnchor":108},"656.6","if-error only",[131,132],"line",{"x1":133,"y1":134,"x2":135,"y2":134,"stroke":81,"style":136},"101.2","96.0","726.0","stroke-opacity:0.4",[84,138,142],{"x":139,"y":140,"fill":141,"style":107,"textAnchor":108},"114.7","113.0","#51617a","0min",[84,144,146],{"x":145,"y":140,"fill":141,"style":107,"textAnchor":108},"205.3","15min",[84,148,150],{"x":149,"y":140,"fill":141,"style":107,"textAnchor":108},"309.5","30min",[84,152,154],{"x":153,"y":140,"fill":141,"style":107,"textAnchor":108},"413.6","45min",[84,156,158],{"x":157,"y":140,"fill":141,"style":107,"textAnchor":108},"517.7","60min",[84,160,162],{"x":161,"y":140,"fill":141,"style":107,"textAnchor":108},"621.9","75min",[84,164,166],{"x":165,"y":140,"fill":141,"style":107,"textAnchor":108},"715.1","90min",[84,168,171],{"x":86,"y":169,"fill":141,"style":170},"144.0","font-size:12.5px","Requests in the SWR window get an instant response and trigger one background refresh; after it, only errors justify",[84,173,175],{"x":86,"y":174,"fill":141,"style":170},"162.0","serving stale.",[177,178,180],"h2",{"id":179},"rapid-diagnosis","Rapid Diagnosis",[182,183,184,192,198,204],"ul",{},[185,186,187,191],"li",{},[188,189,190],"strong",{},"Measure request frequency per object."," An object requested every few seconds stays fresh with short windows; one requested every few minutes needs a long SWR window to ever be served stale instead of missing.",[185,193,194,197],{},[188,195,196],{},"Measure change frequency."," How often does the content actually change? Most pages change far less often than their TTLs assume.",[185,199,200,203],{},[188,201,202],{},"Ask the business about staleness tolerance."," Is a price 5 minutes old acceptable? A headline? A stock count?",[185,205,206,209],{},[188,207,208],{},"Check whether purging exists."," With reliable purges, windows can be generous; without them, windows bound staleness.",[177,211,213],{"id":212},"root-cause-analysis-why-defaults-fail","Root Cause Analysis: Why Defaults Fail",[15,215,216,219],{},[188,217,218],{},"1. Short TTLs without SWR."," Every expiry produces a blocking miss for the next visitor; low-traffic objects almost always miss.",[15,221,222,225],{},[188,223,224],{},"2. Huge SWR without purging."," Content can be served long after it changed, because a background refresh only happens when a request arrives.",[15,227,228,231],{},[188,229,230],{},"3. No stale-if-error."," An origin outage turns cached content into errors as soon as TTLs expire.",[15,233,234,237],{},[188,235,236],{},"4. One policy for everything."," Applying the same windows to HTML, APIs and assets ignores their different change rates.",[15,239,240],{},[52,241,59,244,59,247,59,250,59,252,59,255,59,257,59,261,59,267,59,271,59,274,59,277,59,280,59,283,59,287,59,291,59,295,59,297,59,301,59,303,59,306,59,308,59,311,59,314,59,318,59,320,59,323,59,325,59,328,59,330,59,332,59,335,59,339,59,341,59,344,59,346,59,349,59,351,59,354,59,357,59,361,59,363,59,366,59,368,59,371,59,373,59],{"viewBox":242,"width":55,"role":56,"ariaLabel":243,"style":58},"0 0 760 228","Starting values for s-maxage, stale-while-revalidate and stale-if-error for different kinds of content, assuming purge on change where noted.",[61,245],{"className":246,"x":65,"y":65,"width":55,"height":55,"fill":66},[64],[68,248,249],{},"Suggested windows by content type",[72,251,243],{},[61,253],{"x":76,"y":76,"width":77,"height":254,"rx":79,"fill":80,"stroke":81,"style":82},"226",[84,256,249],{"x":86,"y":87,"fill":81,"style":88},[61,258],{"x":86,"y":97,"width":259,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},"188.0","30.0",[84,262,266],{"x":263,"y":264,"fill":81,"style":92,"textAnchor":265},"38.0","75.5","start","Content",[61,268],{"x":269,"y":97,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},"216.0","172.0",[84,272,44],{"x":273,"y":264,"fill":81,"style":92,"textAnchor":108},"302.0",[61,275],{"x":276,"y":97,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},"388.0",[84,278,120],{"x":279,"y":264,"fill":81,"style":92,"textAnchor":108},"474.0",[61,281],{"x":282,"y":97,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},"560.0",[84,284,286],{"x":285,"y":264,"fill":81,"style":92,"textAnchor":108},"646.0","stale-if-error",[61,288],{"x":86,"y":289,"width":259,"height":260,"rx":65,"fill":80,"stroke":81,"style":290},"86.0","stroke-opacity:0.35",[84,292,294],{"x":263,"y":293,"fill":81,"style":92,"textAnchor":265},"105.5","Article \u002F docs HTML (purged)",[61,296],{"x":269,"y":289,"width":270,"height":260,"rx":65,"fill":101,"stroke":101,"style":102},[84,298,300],{"x":273,"y":293,"fill":81,"style":299,"textAnchor":108},"font-size:12px","1-24 h",[61,302],{"x":276,"y":289,"width":270,"height":260,"rx":65,"fill":101,"stroke":101,"style":102},[84,304,305],{"x":279,"y":293,"fill":81,"style":299,"textAnchor":108},"1 day",[61,307],{"x":282,"y":289,"width":270,"height":260,"rx":65,"fill":101,"stroke":101,"style":102},[84,309,310],{"x":285,"y":293,"fill":81,"style":299,"textAnchor":108},"7 days",[61,312],{"x":86,"y":313,"width":259,"height":260,"rx":65,"fill":80,"stroke":81,"style":290},"116.0",[84,315,317],{"x":263,"y":316,"fill":81,"style":92,"textAnchor":265},"135.5","Homepage \u002F listings",[61,319],{"x":269,"y":313,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},[84,321,322],{"x":273,"y":316,"fill":81,"style":299,"textAnchor":108},"1-5 min",[61,324],{"x":276,"y":313,"width":270,"height":260,"rx":65,"fill":101,"stroke":101,"style":102},[84,326,327],{"x":279,"y":316,"fill":81,"style":299,"textAnchor":108},"10-60 min",[61,329],{"x":282,"y":313,"width":270,"height":260,"rx":65,"fill":101,"stroke":101,"style":102},[84,331,305],{"x":285,"y":316,"fill":81,"style":299,"textAnchor":108},[61,333],{"x":86,"y":334,"width":259,"height":260,"rx":65,"fill":80,"stroke":81,"style":290},"146.0",[84,336,338],{"x":263,"y":337,"fill":81,"style":92,"textAnchor":265},"165.5","Price \u002F stock API",[61,340],{"x":269,"y":334,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},[84,342,343],{"x":273,"y":337,"fill":81,"style":299,"textAnchor":108},"10-60 s",[61,345],{"x":276,"y":334,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},[84,347,348],{"x":279,"y":337,"fill":81,"style":299,"textAnchor":108},"30-120 s",[61,350],{"x":282,"y":334,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},[84,352,353],{"x":285,"y":337,"fill":81,"style":299,"textAnchor":108},"5-15 min",[61,355],{"x":86,"y":356,"width":259,"height":260,"rx":65,"fill":80,"stroke":81,"style":290},"176.0",[84,358,360],{"x":263,"y":359,"fill":81,"style":92,"textAnchor":265},"195.5","Config \u002F feature flags",[61,362],{"x":269,"y":356,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},[84,364,365],{"x":273,"y":359,"fill":81,"style":299,"textAnchor":108},"30-60 s",[61,367],{"x":276,"y":356,"width":270,"height":260,"rx":65,"fill":81,"stroke":81,"style":125},[84,369,370],{"x":279,"y":359,"fill":81,"style":299,"textAnchor":108},"5 min",[61,372],{"x":282,"y":356,"width":270,"height":260,"rx":65,"fill":101,"stroke":101,"style":102},[84,374,305],{"x":285,"y":359,"fill":81,"style":299,"textAnchor":108},[177,376,378],{"id":377},"step-by-step-sizing-the-windows","Step-by-Step: Sizing the Windows",[380,381,383],"h3",{"id":382},"_1-start-from-staleness-tolerance","1. Start from staleness tolerance",[15,385,386,387,390],{},"The maximum age a user can see is roughly ",[30,388,389],{},"s-maxage + stale-while-revalidate"," (plus propagation of any background refresh). Choose that sum from the business tolerance, then split it: a short fresh window, a longer SWR window.",[380,392,394],{"id":393},"_2-check-traffic-per-object-against-the-windows","2. Check traffic per object against the windows",[15,396,397,398,400],{},"If an object is requested less often than once per ",[30,399,389],{},", it will usually miss. Either lengthen the SWR window or add tiered caching so requests aggregate.",[402,403,408],"pre",{"className":404,"code":405,"language":406,"meta":407,"style":407},"language-sql shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","-- Requests per object per hour, to compare with your windows (CDN logs).\nSELECT cache_key, COUNT(*) \u002F 24 AS req_per_hour\nFROM cdn_logs WHERE ts > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY) AND content_type LIKE 'text\u002Fhtml%'\nGROUP BY 1 ORDER BY req_per_hour;\n-- trade-off: this ignores how traffic is spread across edge locations. With many\n-- PoPs and no tiering, per-location rates are much lower than these totals.\n","sql","",[30,409,410,417,423,429,435,441],{"__ignoreMap":407},[411,412,414],"span",{"class":131,"line":413},1,[411,415,416],{},"-- Requests per object per hour, to compare with your windows (CDN logs).\n",[411,418,420],{"class":131,"line":419},2,[411,421,422],{},"SELECT cache_key, COUNT(*) \u002F 24 AS req_per_hour\n",[411,424,426],{"class":131,"line":425},3,[411,427,428],{},"FROM cdn_logs WHERE ts > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY) AND content_type LIKE 'text\u002Fhtml%'\n",[411,430,432],{"class":131,"line":431},4,[411,433,434],{},"GROUP BY 1 ORDER BY req_per_hour;\n",[411,436,438],{"class":131,"line":437},5,[411,439,440],{},"-- trade-off: this ignores how traffic is spread across edge locations. With many\n",[411,442,444],{"class":131,"line":443},6,[411,445,446],{},"-- PoPs and no tiering, per-location rates are much lower than these totals.\n",[380,448,450],{"id":449},"_3-purge-on-change-so-windows-can-be-long","3. Purge on change so windows can be long",[15,452,453],{},"With tag- or URL-based purging on publish, staleness is no longer bounded by the windows, so you can set long SWR windows and get near-100% hit rates.",[380,455,457],{"id":456},"_4-always-add-stale-if-error","4. Always add stale-if-error",[402,459,463],{"className":460,"code":461,"language":462,"meta":407,"style":407},"language-http shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=3600, stale-if-error=86400\n","http",[30,464,465],{"__ignoreMap":407},[411,466,467],{"class":131,"line":413},[411,468,461],{},[15,470,471,472,474],{},"A day of ",[30,473,286],{}," keeps the site up through most origin incidents. (No comments are possible in headers; the trade-off is that during an outage users may see content up to a day old — usually far better than an error page.)",[15,476,477],{},[52,478,59,481,59,484,59,487,59,489,59,492,59,494,59,500,59,506,59,511,59,515,59,520,59,524,59,528,59,532,59],{"viewBox":479,"width":55,"role":56,"ariaLabel":480,"style":58},"0 0 760 163","Bar chart of edge hit ratio for low-traffic article pages as stale-while-revalidate windows are lengthened.",[61,482],{"className":483,"x":65,"y":65,"width":55,"height":55,"fill":66},[64],[68,485,486],{},"HTML hit ratio for low-traffic articles by window",[72,488,480],{},[61,490],{"x":76,"y":76,"width":77,"height":491,"rx":79,"fill":80,"stroke":81,"style":82},"161",[84,493,486],{"x":86,"y":87,"fill":81,"style":88},[84,495,499],{"x":359,"y":496,"fill":81,"style":497,"textAnchor":498},"70.0","font-size:13px","end","s-maxage=60, no SWR",[61,501],{"x":502,"y":97,"width":503,"height":504,"rx":100,"fill":114,"stroke":115,"style":505},"207.5","119.0","19","fill-opacity:0.7;stroke-opacity:0.9",[84,507,510],{"x":508,"y":496,"fill":81,"style":509},"332.5","font-size:12px;font-weight:600","23%",[84,512,514],{"x":359,"y":513,"fill":81,"style":497,"textAnchor":498},"101.0","+ SWR 10 min",[61,516],{"x":502,"y":517,"width":518,"height":504,"rx":100,"fill":101,"stroke":101,"style":519},"87.0","263.9","fill-opacity:0.55;stroke-opacity:0.9",[84,521,523],{"x":522,"y":513,"fill":81,"style":509},"477.4","51%",[84,525,527],{"x":359,"y":526,"fill":81,"style":497,"textAnchor":498},"132.0","+ SWR 1 day (purged on edit)",[61,529],{"x":502,"y":530,"width":531,"height":504,"rx":100,"fill":101,"stroke":101,"style":519},"118.0","460.5",[84,533,535],{"x":534,"y":526,"fill":81,"style":509},"674.0","89%",[177,537,539],{"id":538},"verification","Verification",[15,541,542,543,546,547,549,550,552],{},"After changing windows, monitor hit ratio and the ",[30,544,545],{},"Age"," distribution of served responses: ",[30,548,545],{}," should rarely exceed your tolerance for content without purges. Test ",[30,551,286],{}," by temporarily blocking the origin in staging and confirming cached pages still serve. In RUM, TTFB p75 should improve most for low-traffic pages.",[177,554,556],{"id":555},"worked-example-tuning-a-news-site","Worked Example: Tuning a News Site",[15,558,559,560,563,564,567,568,571],{},"A news site used ",[30,561,562],{},"s-maxage=60"," with no SWR on all HTML. Breaking news pages were fine — requested constantly — but the long tail of older articles missed 80% of the time, with TTFB p75 of 700ms for those visits. The team kept a 60-second fresh window for the homepage with a 10-minute SWR window, set articles to ",[30,565,566],{},"s-maxage=3600, stale-while-revalidate=86400"," with purge-on-edit, and added ",[30,569,570],{},"stale-if-error=86400"," everywhere. Article hit ratio rose to 91%, TTFB for rarely read articles p75 dropped to 90ms, and during a 40-minute origin outage the following month, readers saw cached pages instead of errors.",[177,573,575],{"id":574},"browser-side-swr","Browser-Side SWR",[15,577,578,579,581,582,585,586,589],{},"Browsers also implement ",[30,580,120],{}," in ",[30,583,584],{},"Cache-Control"," for their HTTP cache. For assets with stable names and APIs called repeatedly, a small browser SWR window (",[30,587,588],{},"max-age=60, stale-while-revalidate=300",") lets repeat requests resolve instantly while refreshing in the background. Use it cautiously for HTML: browser caches cannot be purged, so the browser window directly bounds how stale a page can appear after a deploy. For HTML, keep browser SWR off and rely on the CDN's windows.",[177,591,593],{"id":592},"common-mistakes","Common Mistakes",[182,595,596,602,608,614],{},[185,597,598,601],{},[188,599,600],{},"Very long windows without purges."," Edits may take hours or days to appear.",[185,603,604,607],{},[188,605,606],{},"Forgetting that SWR needs a request to trigger."," Content not requested during the window is never refreshed; the next visitor after the window gets a full miss.",[185,609,610,613],{},[188,611,612],{},"Identical windows for volatile APIs."," Prices and stock need short windows or event-driven purges.",[185,615,616,619],{},[188,617,618],{},"Expecting SWR to fix uncacheable responses."," Private or cookie-setting responses are not cached at all.",[177,621,623],{"id":622},"edge-cases","Edge Cases",[15,625,626,629,630,632,633,635],{},[188,627,628],{},"CDN support."," Most major CDNs honour ",[30,631,120],{}," and ",[30,634,286],{},"; some use vendor-specific equivalents (for example, Fastly's surrogate controls). Check your CDN's behaviour.",[15,637,638,641],{},[188,639,640],{},"Concurrent revalidation."," Good CDNs send one background request per object; poor implementations may send many under load.",[15,643,644,647],{},[188,645,646],{},"Service workers."," Workbox's StaleWhileRevalidate strategy is a separate, client-side implementation; align its behaviour with your headers.",[15,649,650,653],{},[188,651,652],{},"Errors during revalidation."," If the background refresh fails, the cache keeps serving stale (within the SWR window) and retries on the next request.",[177,655,657],{"id":656},"faq","FAQ",[659,660,663,667],"details",{"className":661},[662],"faq-item",[664,665,666],"summary",{},"Does stale-while-revalidate delay updates after a purge?",[15,668,669],{},"No. A purge removes the entry; the next request is a miss that fetches fresh content. SWR only affects entries that expired naturally.",[659,671,673,676],{"className":672},[662],[664,674,675],{},"Is there a maximum useful SWR window?",[15,677,678],{},"For purged content, windows of a day or more are common. Beyond that, gains are small because most objects are refreshed by traffic or purges sooner, and eviction policies may remove rarely used entries anyway.",[659,680,682,685],{"className":681},[662],[664,683,684],{},"Should APIs and HTML share a policy?",[15,686,687],{},"Rarely. APIs often change more frequently and are consumed by code that can handle refreshes; HTML is consumed by people and benefits from longer windows plus purges.",[659,689,691,694],{"className":690},[662],[664,692,693],{},"How do I measure staleness actually served?",[15,695,696,697,699],{},"Log the ",[30,698,545],{}," header (or compute content age from timestamps embedded in responses) at the edge and chart its distribution per content type.",[659,701,703,706],{"className":702},[662],[664,704,705],{},"What about stale-if-error for personalised content?",[15,707,708],{},"It only applies to cached responses; personalised responses that are not cached cannot be served stale. For resilience there, use fallbacks in the application.",[659,710,712,715],{"className":711},[662],[664,713,714],{},"Does SWR improve LCP or only TTFB?",[15,716,717],{},"It improves TTFB directly, and LCP for server-rendered pages in proportion, because more requests are served from cache instead of waiting for the origin.",[177,719,721],{"id":720},"related","Related",[182,723,724,731,738],{},[185,725,726,730],{},[19,727,729],{"href":728},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fswr-cache-control-vs-service-worker-revalidation\u002F","SWR Cache-Control vs Service Worker revalidation"," — the same idea at two layers.",[185,732,733,737],{},[19,734,736],{"href":735},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002Fconfiguring-stale-if-error-for-origin-outages\u002F","Configuring stale-if-error for origin outages"," — the resilience window in depth.",[185,739,740,744],{},[19,741,743],{"href":742},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Fpurging-cdn-cache-by-tag-on-deploy\u002F","Purging CDN cache by tag on deploy"," — what makes long windows safe.",[746,747,749],"script",{"type":748},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Choose stale-while-revalidate Windows for HTML and APIs\",\n  \"description\": \"A method for choosing max-age, stale-while-revalidate and stale-if-error values that maximise hit rates without showing users unacceptably old content.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Start from staleness tolerance\",\n      \"text\": \"The maximum age a user can see is roughly s-maxage + stale-while-revalidate (plus propagation of any background refresh).\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Check traffic per object against the windows\",\n      \"text\": \"If an object is requested less often than once per s-maxage + stale-while-revalidate, it will usually miss.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Purge on change so windows can be long\",\n      \"text\": \"With tag- or URL-based purging on publish, staleness is no longer bounded by the windows, so you can set long SWR windows and get near-100% hit rates.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Always add stale-if-error\",\n      \"text\": \"A day of stale-if-error keeps the site up through most origin incidents.\"\n    }\n  ]\n}\n",[746,751,752],{"type":748},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Choose stale-while-revalidate Windows for HTML and APIs\",\n  \"description\": \"A method for choosing max-age, stale-while-revalidate and stale-if-error values that maximise hit rates without showing users unacceptably old content.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis\u002F\"\n  }\n}\n",[746,754,755],{"type":748},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Advanced Caching Strategies & CDN Architecture\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Stale-While-Revalidate Implementation\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Choosing SWR Windows for HTML and APIs\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis\u002F\"\n    }\n  ]\n}\n",[757,758,759],"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);}",{"title":407,"searchDepth":419,"depth":419,"links":761},[762,763,764,770,771,772,773,774,775,776],{"id":179,"depth":419,"text":180},{"id":212,"depth":419,"text":213},{"id":377,"depth":419,"text":378,"children":765},[766,767,768,769],{"id":382,"depth":425,"text":383},{"id":393,"depth":425,"text":394},{"id":449,"depth":425,"text":450},{"id":456,"depth":425,"text":457},{"id":538,"depth":419,"text":539},{"id":555,"depth":419,"text":556},{"id":574,"depth":419,"text":575},{"id":592,"depth":419,"text":593},{"id":622,"depth":419,"text":623},{"id":656,"depth":419,"text":657},{"id":720,"depth":419,"text":721},"A method for choosing max-age, stale-while-revalidate and stale-if-error values that maximise hit rates without showing users unacceptably old content.","md",{"slug":780,"type":781,"breadcrumb":782,"datePublished":789,"dateModified":789},"choosing-swr-windows-for-html-and-apis","article",[783,785,786,787],{"name":784,"url":41},"Home",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":788},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis\u002F","2026-10-06",true,"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis",{"title":793,"description":794},"Choosing stale-while-revalidate Windows for HTML and APIs","How long should stale-while-revalidate and stale-if-error windows be? Size them from traffic, change frequency and tolerance for staleness, per content type.","advanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fchoosing-swr-windows-for-html-and-apis\u002Findex","-6IaMz5fpaWQPjiARfEx59_V8scn0k4DOL6NNxpdqJQ",[798,801],{"title":22,"path":799,"stem":800,"children":-1},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation","advanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Findex",{"title":802,"path":803,"stem":804,"children":-1},"Stale-While-Revalidate Data Fetching with SWR and TanStack Query","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fstale-while-revalidate-data-fetching-with-swr-and-tanstack-query","advanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Fstale-while-revalidate-data-fetching-with-swr-and-tanstack-query\u002Findex",1791308074949]