[{"data":1,"prerenderedAt":1556},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002F":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002F":1547},{"id":4,"title":5,"body":6,"description":1540,"extension":1541,"meta":1542,"navigation":247,"path":1543,"seo":1544,"stem":1545,"__hash__":1546},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Findex.md","Stale-While-Revalidate Implementation: A Production Blueprint for Frontend Performance",{"type":7,"value":8,"toc":1527},"minimark",[9,13,23,28,47,53,95,113,118,134,138,147,152,155,287,301,305,308,516,520,523,716,725,729,739,996,1014,1028,1032,1035,1040,1105,1111,1116,1159,1163,1166,1174,1291,1309,1322,1339,1343,1440,1444,1466,1478,1499,1523],[10,11,5],"h1",{"id":12},"stale-while-revalidate-implementation-a-production-blueprint-for-frontend-performance",[14,15,16,17,22],"p",{},"Stale-While-Revalidate (SWR) is a critical HTTP caching directive that decouples response latency from data freshness, enabling instant delivery of cached assets while silently updating them in the background. This guide provides a production-ready blueprint for implementing SWR across HTTP headers, CDN edge layers, and client-side service workers. While foundational caching architecture is documented in ",[18,19,21],"a",{"href":20},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",", this article focuses exclusively on configuration syntax, diagnostic workflows, and explicit metric thresholds required to optimize Core Web Vitals without introducing cache-stampede risks or stale-content penalties.",[24,25,27],"h2",{"id":26},"the-mechanics-of-stale-while-revalidate-in-modern-browsers","The Mechanics of stale-while-revalidate in Modern Browsers",[14,29,30,31,35,36,39,40,43,44,46],{},"The ",[32,33,34],"code",{},"stale-while-revalidate"," directive, standardized in RFC 5861, instructs caches to serve a stale response immediately while asynchronously fetching a fresh copy from the origin. Modern browsers parse ",[32,37,38],{},"Cache-Control"," directives in strict order, evaluating ",[32,41,42],{},"max-age"," first to determine the freshness window, then applying ",[32,45,34],{}," to define the grace period during which background revalidation is permitted.",[14,48,49],{},[50,51,52],"strong",{},"Execution Timeline:",[54,55,56,70,89],"ol",{},[57,58,59,69],"li",{},[50,60,61,62,65,66,68],{},"Fresh Window (",[32,63,64],{},"0"," to ",[32,67,42],{},"):"," Requests are served directly from cache. Zero network latency.",[57,71,72,80,81,84,85,88],{},[50,73,74,75,65,77,68],{},"Stale Window (",[32,76,42],{},[32,78,79],{},"max-age + stale-while-revalidate"," The browser serves the cached response synchronously (TTFB \u003C 10ms) and triggers a background ",[32,82,83],{},"GET"," to the origin. The network request runs at ",[32,86,87],{},"Low"," priority to avoid blocking critical rendering paths.",[57,90,91,94],{},[50,92,93],{},"Post-Stale Expiration:"," Once the stale window closes, subsequent requests block until the origin responds, reverting to standard cache-miss behavior.",[14,96,97,98,100,101,104,105,107,108,112],{},"During the stale window, concurrent requests for the same resource are deduplicated by the browser's HTTP cache layer. Only one background fetch executes, and all pending requests receive the updated payload once it resolves. If multiple ",[32,99,38],{}," directives conflict (e.g., ",[32,102,103],{},"no-cache"," alongside ",[32,106,34],{},"), browsers prioritize explicit invalidation directives. For a complete breakdown of header validation logic and precedence, refer to ",[18,109,111],{"href":110},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002F","HTTP Cache-Control Headers Explained",".",[14,114,115],{},[50,116,117],{},"Performance Impact Mapping:",[119,120,121,126,131],"ul",{},[57,122,123,125],{},[32,124,42],{}," directly controls cache hit rate.",[57,127,128,130],{},[32,129,34],{}," directly controls background network overhead and data freshness lag.",[57,132,133],{},"Misconfiguration here directly impacts LCP (if blocking) or INP (if background fetches compete with main-thread tasks).",[24,135,137],{"id":136},"server-side-cdn-configuration-nginx-express-and-cloudflare","Server-Side & CDN Configuration: Nginx, Express, and Cloudflare",[14,139,140,141,143,144,146],{},"Implementing SWR requires precise TTL alignment across the origin server, edge proxy, and browser cache. Misaligned TTLs (e.g., CDN ",[32,142,42],{}," > origin ",[32,145,42],{},") cause edge caches to serve stale data beyond the intended window, breaking consistency guarantees.",[148,149,151],"h3",{"id":150},"dynamic-header-injection-by-asset-type-nginx","Dynamic Header Injection by Asset Type (Nginx)",[14,153,154],{},"Use conditional mapping to apply aggressive SWR windows to immutable static assets while keeping dynamic API responses tightly scoped.",[156,157,162],"pre",{"className":158,"code":159,"language":160,"meta":161,"style":161},"language-nginx shiki shiki-themes github-dark-high-contrast github-dark-high-contrast github-light-high-contrast","map $content_type $swr_ttl {\n default \"max-age=60, stale-while-revalidate=300\";\n ~image\u002F \"max-age=31536000, stale-while-revalidate=86400\";\n ~text\u002Fcss \"max-age=31536000, stale-while-revalidate=86400\";\n ~application\u002Fjavascript \"max-age=31536000, stale-while-revalidate=86400\";\n}\n\nserver {\n # ...\n add_header Cache-Control $swr_ttl always;\n add_header Vary Accept-Encoding always;\n}\n","nginx","",[32,163,164,184,198,212,224,236,242,249,258,265,274,282],{"__ignoreMap":161},[165,166,169,173,177,181],"span",{"class":167,"line":168},"line",1,[165,170,172],{"class":171},"sCJTb","map",[165,174,176],{"class":175},"s3sCt"," $",[165,178,180],{"class":179},"spFnL","content_type",[165,182,183],{"class":175}," $swr_ttl {\n",[165,185,187,191,195],{"class":167,"line":186},2,[165,188,190],{"class":189},"s5hCx"," default",[165,192,194],{"class":193},"sJdzJ"," \"max-age=60, stale-while-revalidate=300\"",[165,196,197],{"class":175},";\n",[165,199,201,204,207,210],{"class":167,"line":200},3,[165,202,203],{"class":171}," ~",[165,205,206],{"class":175},"image\u002F ",[165,208,209],{"class":193},"\"max-age=31536000, stale-while-revalidate=86400\"",[165,211,197],{"class":175},[165,213,215,217,220,222],{"class":167,"line":214},4,[165,216,203],{"class":171},[165,218,219],{"class":175},"text\u002Fcss ",[165,221,209],{"class":193},[165,223,197],{"class":175},[165,225,227,229,232,234],{"class":167,"line":226},5,[165,228,203],{"class":171},[165,230,231],{"class":175},"application\u002Fjavascript ",[165,233,209],{"class":193},[165,235,197],{"class":175},[165,237,239],{"class":167,"line":238},6,[165,240,241],{"class":175},"}\n",[165,243,245],{"class":167,"line":244},7,[165,246,248],{"emptyLinePlaceholder":247},true,"\n",[165,250,252,255],{"class":167,"line":251},8,[165,253,254],{"class":171},"server",[165,256,257],{"class":175}," {\n",[165,259,261],{"class":167,"line":260},9,[165,262,264],{"class":263},"sXJMR"," # ...\n",[165,266,268,271],{"class":167,"line":267},10,[165,269,270],{"class":171}," add_header ",[165,272,273],{"class":175},"Cache-Control $swr_ttl always;\n",[165,275,277,279],{"class":167,"line":276},11,[165,278,270],{"class":171},[165,280,281],{"class":175},"Vary Accept-Encoding always;\n",[165,283,285],{"class":167,"line":284},12,[165,286,241],{"class":175},[14,288,289,293,294,297,298,300],{},[290,291,292],"em",{},"Trade-off:"," The ",[32,295,296],{},"Vary"," header prevents compression-related cache fragmentation but increases cache key cardinality. Ensure your CDN supports ",[32,299,296],{}," normalization.",[148,302,304],{"id":303},"route-specific-middleware-expressjs","Route-Specific Middleware (Express.js)",[14,306,307],{},"For Node.js backends, apply granular control via middleware to prevent over-caching of personalized or transactional endpoints.",[156,309,313],{"className":310,"code":311,"language":312,"meta":161,"style":161},"language-javascript shiki shiki-themes github-dark-high-contrast github-dark-high-contrast github-light-high-contrast","const swrMiddleware = (req, res, next) => {\n const route = req.path;\n if (route.startsWith('\u002Fapi\u002Fv1\u002Fpublic')) {\n \u002F\u002F Public data: 2min fresh, 10min stale background refresh\n res.set('Cache-Control', 'public, max-age=120, stale-while-revalidate=600');\n } else if (route.startsWith('\u002Fassets\u002F')) {\n \u002F\u002F Versioned bundles: 1yr fresh, 30d stale\n res.set('Cache-Control', 'public, max-age=31536000, stale-while-revalidate=2592000');\n } else {\n \u002F\u002F Fallback: strict validation\n res.set('Cache-Control', 'no-cache, no-store, must-revalidate');\n }\n next();\n};\napp.use(swrMiddleware);\n","javascript",[32,314,315,352,365,385,390,411,432,437,454,462,467,484,489,498,504],{"__ignoreMap":161},[165,316,317,320,324,327,330,333,336,339,341,344,347,350],{"class":167,"line":168},[165,318,319],{"class":171},"const",[165,321,323],{"class":322},"sGhOu"," swrMiddleware",[165,325,326],{"class":171}," =",[165,328,329],{"class":175}," (",[165,331,332],{"class":179},"req",[165,334,335],{"class":175},", ",[165,337,338],{"class":179},"res",[165,340,335],{"class":175},[165,342,343],{"class":179},"next",[165,345,346],{"class":175},") ",[165,348,349],{"class":171},"=>",[165,351,257],{"class":175},[165,353,354,357,360,362],{"class":167,"line":186},[165,355,356],{"class":171}," const",[165,358,359],{"class":189}," route",[165,361,326],{"class":171},[165,363,364],{"class":175}," req.path;\n",[165,366,367,370,373,376,379,382],{"class":167,"line":200},[165,368,369],{"class":171}," if",[165,371,372],{"class":175}," (route.",[165,374,375],{"class":322},"startsWith",[165,377,378],{"class":175},"(",[165,380,381],{"class":193},"'\u002Fapi\u002Fv1\u002Fpublic'",[165,383,384],{"class":175},")) {\n",[165,386,387],{"class":167,"line":214},[165,388,389],{"class":263}," \u002F\u002F Public data: 2min fresh, 10min stale background refresh\n",[165,391,392,395,398,400,403,405,408],{"class":167,"line":226},[165,393,394],{"class":175}," res.",[165,396,397],{"class":322},"set",[165,399,378],{"class":175},[165,401,402],{"class":193},"'Cache-Control'",[165,404,335],{"class":175},[165,406,407],{"class":193},"'public, max-age=120, stale-while-revalidate=600'",[165,409,410],{"class":175},");\n",[165,412,413,416,419,421,423,425,427,430],{"class":167,"line":238},[165,414,415],{"class":175}," } ",[165,417,418],{"class":171},"else",[165,420,369],{"class":171},[165,422,372],{"class":175},[165,424,375],{"class":322},[165,426,378],{"class":175},[165,428,429],{"class":193},"'\u002Fassets\u002F'",[165,431,384],{"class":175},[165,433,434],{"class":167,"line":244},[165,435,436],{"class":263}," \u002F\u002F Versioned bundles: 1yr fresh, 30d stale\n",[165,438,439,441,443,445,447,449,452],{"class":167,"line":251},[165,440,394],{"class":175},[165,442,397],{"class":322},[165,444,378],{"class":175},[165,446,402],{"class":193},[165,448,335],{"class":175},[165,450,451],{"class":193},"'public, max-age=31536000, stale-while-revalidate=2592000'",[165,453,410],{"class":175},[165,455,456,458,460],{"class":167,"line":260},[165,457,415],{"class":175},[165,459,418],{"class":171},[165,461,257],{"class":175},[165,463,464],{"class":167,"line":267},[165,465,466],{"class":263}," \u002F\u002F Fallback: strict validation\n",[165,468,469,471,473,475,477,479,482],{"class":167,"line":276},[165,470,394],{"class":175},[165,472,397],{"class":322},[165,474,378],{"class":175},[165,476,402],{"class":193},[165,478,335],{"class":175},[165,480,481],{"class":193},"'no-cache, no-store, must-revalidate'",[165,483,410],{"class":175},[165,485,486],{"class":167,"line":284},[165,487,488],{"class":175}," }\n",[165,490,492,495],{"class":167,"line":491},13,[165,493,494],{"class":322}," next",[165,496,497],{"class":175},"();\n",[165,499,501],{"class":167,"line":500},14,[165,502,503],{"class":175},"};\n",[165,505,507,510,513],{"class":167,"line":506},15,[165,508,509],{"class":175},"app.",[165,511,512],{"class":322},"use",[165,514,515],{"class":175},"(swrMiddleware);\n",[148,517,519],{"id":518},"edge-override-cloudflare-workers","Edge Override (Cloudflare Workers)",[14,521,522],{},"When origin control is limited, intercept and rewrite headers at the CDN edge. This decouples frontend performance tuning from backend deployment cycles.",[156,524,526],{"className":310,"code":525,"language":312,"meta":161,"style":161},"addEventListener('fetch', event => {\n event.respondWith(handleRequest(event.request));\n});\n\nasync function handleRequest(request) {\n const response = await fetch(request);\n const headers = new Headers(response.headers);\n \n if (request.url.includes('\u002Fstatic\u002F')) {\n headers.set('Cache-Control', 'public, max-age=31536000, stale-while-revalidate=604800');\n }\n \n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: headers\n });\n}\n",[32,527,528,548,564,569,573,592,610,628,633,650,668,672,676,689,694,699,705,711],{"__ignoreMap":161},[165,529,530,533,535,538,540,543,546],{"class":167,"line":168},[165,531,532],{"class":322},"addEventListener",[165,534,378],{"class":175},[165,536,537],{"class":193},"'fetch'",[165,539,335],{"class":175},[165,541,542],{"class":179},"event",[165,544,545],{"class":171}," =>",[165,547,257],{"class":175},[165,549,550,553,556,558,561],{"class":167,"line":186},[165,551,552],{"class":175}," event.",[165,554,555],{"class":322},"respondWith",[165,557,378],{"class":175},[165,559,560],{"class":322},"handleRequest",[165,562,563],{"class":175},"(event.request));\n",[165,565,566],{"class":167,"line":200},[165,567,568],{"class":175},"});\n",[165,570,571],{"class":167,"line":214},[165,572,248],{"emptyLinePlaceholder":247},[165,574,575,578,581,584,586,589],{"class":167,"line":226},[165,576,577],{"class":171},"async",[165,579,580],{"class":171}," function",[165,582,583],{"class":322}," handleRequest",[165,585,378],{"class":175},[165,587,588],{"class":179},"request",[165,590,591],{"class":175},") {\n",[165,593,594,596,599,601,604,607],{"class":167,"line":238},[165,595,356],{"class":171},[165,597,598],{"class":189}," response",[165,600,326],{"class":171},[165,602,603],{"class":171}," await",[165,605,606],{"class":322}," fetch",[165,608,609],{"class":175},"(request);\n",[165,611,612,614,617,619,622,625],{"class":167,"line":244},[165,613,356],{"class":171},[165,615,616],{"class":189}," headers",[165,618,326],{"class":171},[165,620,621],{"class":171}," new",[165,623,624],{"class":322}," Headers",[165,626,627],{"class":175},"(response.headers);\n",[165,629,630],{"class":167,"line":251},[165,631,632],{"class":175}," \n",[165,634,635,637,640,643,645,648],{"class":167,"line":260},[165,636,369],{"class":171},[165,638,639],{"class":175}," (request.url.",[165,641,642],{"class":322},"includes",[165,644,378],{"class":175},[165,646,647],{"class":193},"'\u002Fstatic\u002F'",[165,649,384],{"class":175},[165,651,652,655,657,659,661,663,666],{"class":167,"line":267},[165,653,654],{"class":175}," headers.",[165,656,397],{"class":322},[165,658,378],{"class":175},[165,660,402],{"class":193},[165,662,335],{"class":175},[165,664,665],{"class":193},"'public, max-age=31536000, stale-while-revalidate=604800'",[165,667,410],{"class":175},[165,669,670],{"class":167,"line":276},[165,671,488],{"class":175},[165,673,674],{"class":167,"line":284},[165,675,632],{"class":175},[165,677,678,681,683,686],{"class":167,"line":491},[165,679,680],{"class":171}," return",[165,682,621],{"class":171},[165,684,685],{"class":322}," Response",[165,687,688],{"class":175},"(response.body, {\n",[165,690,691],{"class":167,"line":500},[165,692,693],{"class":175}," status: response.status,\n",[165,695,696],{"class":167,"line":506},[165,697,698],{"class":175}," statusText: response.statusText,\n",[165,700,702],{"class":167,"line":701},16,[165,703,704],{"class":175}," headers: headers\n",[165,706,708],{"class":167,"line":707},17,[165,709,710],{"class":175}," });\n",[165,712,714],{"class":167,"line":713},18,[165,715,241],{"class":175},[14,717,718,721,722,724],{},[290,719,720],{},"Threshold Recommendation:"," Static assets should use ",[32,723,34],{}," ≤ 30 days to avoid serving deprecated JS\u002FCSS after major deployments. Dynamic APIs should cap stale windows at 10 minutes to maintain acceptable data freshness.",[24,726,728],{"id":727},"service-worker-coordination-cache-api-synchronization","Service Worker Coordination & Cache API Synchronization",[14,730,731,732,735,736,112],{},"HTTP-layer SWR and Service Worker caching operate independently. Browsers evaluate HTTP headers first, but a registered Service Worker intercepts ",[32,733,734],{},"fetch"," events before the HTTP cache. To achieve true background revalidation, you must explicitly implement a cache-first strategy with ",[32,737,738],{},"event.waitUntil()",[156,740,742],{"className":310,"code":741,"language":312,"meta":161,"style":161},"self.addEventListener('fetch', (event) => {\n if (event.request.method !== 'GET') return;\n \n event.respondWith(\n caches.match(event.request).then((cachedResponse) => {\n if (cachedResponse) {\n \u002F\u002F Serve cached response immediately\n const fetchPromise = fetch(event.request).then((networkResponse) => {\n if (networkResponse && networkResponse.ok) {\n return caches.open('v1').then((cache) => {\n \u002F\u002F Atomic cache update\n return cache.put(event.request, networkResponse.clone());\n });\n }\n });\n \u002F\u002F Prevent SW termination before background update completes\n event.waitUntil(fetchPromise);\n return cachedResponse;\n }\n return fetch(event.request);\n })\n );\n});\n",[32,743,744,766,786,790,799,825,832,837,863,876,906,911,930,934,938,942,947,957,964,969,979,985,991],{"__ignoreMap":161},[165,745,746,749,751,753,755,758,760,762,764],{"class":167,"line":168},[165,747,748],{"class":175},"self.",[165,750,532],{"class":322},[165,752,378],{"class":175},[165,754,537],{"class":193},[165,756,757],{"class":175},", (",[165,759,542],{"class":179},[165,761,346],{"class":175},[165,763,349],{"class":171},[165,765,257],{"class":175},[165,767,768,770,773,776,779,781,784],{"class":167,"line":186},[165,769,369],{"class":171},[165,771,772],{"class":175}," (event.request.method ",[165,774,775],{"class":171},"!==",[165,777,778],{"class":193}," 'GET'",[165,780,346],{"class":175},[165,782,783],{"class":171},"return",[165,785,197],{"class":175},[165,787,788],{"class":167,"line":200},[165,789,632],{"class":175},[165,791,792,794,796],{"class":167,"line":214},[165,793,552],{"class":175},[165,795,555],{"class":322},[165,797,798],{"class":175},"(\n",[165,800,801,804,807,810,813,816,819,821,823],{"class":167,"line":226},[165,802,803],{"class":175}," caches.",[165,805,806],{"class":322},"match",[165,808,809],{"class":175},"(event.request).",[165,811,812],{"class":322},"then",[165,814,815],{"class":175},"((",[165,817,818],{"class":179},"cachedResponse",[165,820,346],{"class":175},[165,822,349],{"class":171},[165,824,257],{"class":175},[165,826,827,829],{"class":167,"line":238},[165,828,369],{"class":171},[165,830,831],{"class":175}," (cachedResponse) {\n",[165,833,834],{"class":167,"line":244},[165,835,836],{"class":263}," \u002F\u002F Serve cached response immediately\n",[165,838,839,841,844,846,848,850,852,854,857,859,861],{"class":167,"line":251},[165,840,356],{"class":171},[165,842,843],{"class":189}," fetchPromise",[165,845,326],{"class":171},[165,847,606],{"class":322},[165,849,809],{"class":175},[165,851,812],{"class":322},[165,853,815],{"class":175},[165,855,856],{"class":179},"networkResponse",[165,858,346],{"class":175},[165,860,349],{"class":171},[165,862,257],{"class":175},[165,864,865,867,870,873],{"class":167,"line":260},[165,866,369],{"class":171},[165,868,869],{"class":175}," (networkResponse ",[165,871,872],{"class":171},"&&",[165,874,875],{"class":175}," networkResponse.ok) {\n",[165,877,878,880,882,885,887,890,893,895,897,900,902,904],{"class":167,"line":267},[165,879,680],{"class":171},[165,881,803],{"class":175},[165,883,884],{"class":322},"open",[165,886,378],{"class":175},[165,888,889],{"class":193},"'v1'",[165,891,892],{"class":175},").",[165,894,812],{"class":322},[165,896,815],{"class":175},[165,898,899],{"class":179},"cache",[165,901,346],{"class":175},[165,903,349],{"class":171},[165,905,257],{"class":175},[165,907,908],{"class":167,"line":276},[165,909,910],{"class":263}," \u002F\u002F Atomic cache update\n",[165,912,913,915,918,921,924,927],{"class":167,"line":284},[165,914,680],{"class":171},[165,916,917],{"class":175}," cache.",[165,919,920],{"class":322},"put",[165,922,923],{"class":175},"(event.request, networkResponse.",[165,925,926],{"class":322},"clone",[165,928,929],{"class":175},"());\n",[165,931,932],{"class":167,"line":491},[165,933,710],{"class":175},[165,935,936],{"class":167,"line":500},[165,937,488],{"class":175},[165,939,940],{"class":167,"line":506},[165,941,710],{"class":175},[165,943,944],{"class":167,"line":701},[165,945,946],{"class":263}," \u002F\u002F Prevent SW termination before background update completes\n",[165,948,949,951,954],{"class":167,"line":707},[165,950,552],{"class":175},[165,952,953],{"class":322},"waitUntil",[165,955,956],{"class":175},"(fetchPromise);\n",[165,958,959,961],{"class":167,"line":713},[165,960,680],{"class":171},[165,962,963],{"class":175}," cachedResponse;\n",[165,965,967],{"class":167,"line":966},19,[165,968,488],{"class":175},[165,970,972,974,976],{"class":167,"line":971},20,[165,973,680],{"class":171},[165,975,606],{"class":322},[165,977,978],{"class":175},"(event.request);\n",[165,980,982],{"class":167,"line":981},21,[165,983,984],{"class":175}," })\n",[165,986,988],{"class":167,"line":987},22,[165,989,990],{"class":175}," );\n",[165,992,994],{"class":167,"line":993},23,[165,995,568],{"class":175},[14,997,998,1001,1002,1005,1006,1009,1010,112],{},[50,999,1000],{},"Multi-Tab Race Condition Mitigation:"," When multiple tabs request the same stale resource, each triggers an independent background fetch. To deduplicate at the SW level, use ",[32,1003,1004],{},"BroadcastChannel"," or ",[32,1007,1008],{},"localStorage"," locks to coordinate a single network request across tabs. For advanced fallback routing patterns, consult ",[18,1011,1013],{"href":1012},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002F","Service Worker Caching Strategies",[14,1015,1016,1019,1020,1023,1024,1027],{},[50,1017,1018],{},"Atomic Updates:"," Always ",[32,1021,1022],{},".clone()"," the ",[32,1025,1026],{},"Response"," object before writing to the Cache API. Consuming the body stream during cache storage will break the response delivered to the client.",[24,1029,1031],{"id":1030},"diagnostic-workflows-metric-optimization","Diagnostic Workflows & Metric Optimization",[14,1033,1034],{},"Validating SWR behavior requires moving beyond synthetic Lighthouse audits to real-world telemetry and network waterfall analysis.",[14,1036,1037],{},[50,1038,1039],{},"Step-by-Step Validation:",[54,1041,1042,1063,1072],{},[57,1043,1044,1047,1048,1051,1052,1005,1055,1058,1059,1062],{},[50,1045,1046],{},"Chrome DevTools:"," Open Network tab → Disable cache: ",[32,1049,1050],{},"OFF"," → Reload. Look for ",[32,1053,1054],{},"Status Code: 200 (from service worker)",[32,1056,1057],{},"(from disk cache)",". Subsequent requests within the stale window will show ",[32,1060,1061],{},"Size: 0 B"," (cache hit) with a concurrent background fetch visible in the waterfall.",[57,1064,1065,1068,1069,1071],{},[50,1066,1067],{},"WebPageTest:"," Run a multi-step test. In the waterfall, verify ",[32,1070,38],{}," headers match your config. Check the \"Background Tasks\" timeline to confirm revalidation occurs after TTFB.",[57,1073,1074,1077,1078,1081,1082,1085,1086,1089,1090,1093,1094,1097,1098,1101,1102,112],{},[50,1075,1076],{},"RUM Tracking:"," Instrument ",[32,1079,1080],{},"PerformanceObserver"," for ",[32,1083,1084],{},"resource"," entries. Filter by ",[32,1087,1088],{},"initiatorType === 'fetch'"," and track ",[32,1091,1092],{},"responseStart - requestStart",". Target ",[32,1095,1096],{},"\u003C100ms"," for stale cache hits, ",[32,1099,1100],{},"\u003C200ms"," for background revalidation completion, and maintain ",[32,1103,1104],{},"LCP \u003C 2.5s",[14,1106,1107,1110],{},[50,1108,1109],{},"Cache Priming:"," To eliminate the initial cold-start latency before SWR activates, prime the cache during idle periods. Implementing Preloading key requests with link rel=preload ensures critical assets populate the cache before the user navigates, guaranteeing the first SWR hit is instantaneous.",[14,1112,1113],{},[50,1114,1115],{},"Audit Checklist:",[119,1117,1120,1135,1143,1153],{"className":1118},[1119],"contains-task-list",[57,1121,1124,1128,1129,1131,1132,1134],{"className":1122},[1123],"task-list-item",[1125,1126],"input",{"disabled":247,"type":1127},"checkbox"," ",[32,1130,34],{}," > ",[32,1133,42],{}," (prevents directive invalidation)",[57,1136,1138,1128,1140,1142],{"className":1137},[1123],[1125,1139],{"disabled":247,"type":1127},[32,1141,296],{}," headers match CDN normalization rules",[57,1144,1146,1148,1149,1152],{"className":1145},[1123],[1125,1147],{"disabled":247,"type":1127}," Background fetch priority set to ",[32,1150,1151],{},"low"," (DevTools → Network → Priority column)",[57,1154,1156,1158],{"className":1155},[1123],[1125,1157],{"disabled":247,"type":1127}," RUM alerts configured for stale-content penalties (>10% cache miss rate on SWR routes)",[24,1160,1162],{"id":1161},"threshold-tuning-cache-invalidation-and-edge-case-handling","Threshold Tuning, Cache Invalidation, and Edge Case Handling",[14,1164,1165],{},"SWR is not a universal directive. Its effectiveness depends entirely on content volatility and business criticality.",[14,1167,1168],{},[50,1169,1170,1171,1173],{},"Decision Matrix for ",[32,1172,34],{}," Duration:",[1175,1176,1177,1202],"table",{},[1178,1179,1180],"thead",{},[1181,1182,1183,1187,1190,1195,1199],"tr",{},[1184,1185,1186],"th",{},"Content Type",[1184,1188,1189],{},"Update Frequency",[1184,1191,1192,1193],{},"Recommended ",[32,1194,42],{},[1184,1196,1192,1197],{},[32,1198,34],{},[1184,1200,1201],{},"Risk Tolerance",[1203,1204,1205,1228,1249,1270],"tbody",{},[1181,1206,1207,1211,1214,1220,1226],{},[1208,1209,1210],"td",{},"Versioned Assets (JS\u002FCSS\u002FImages)",[1208,1212,1213],{},"Per deployment",[1208,1215,1216,1219],{},[32,1217,1218],{},"31536000"," (1yr)",[1208,1221,1222,1225],{},[32,1223,1224],{},"86400"," (1d)",[1208,1227,87],{},[1181,1229,1230,1233,1236,1241,1246],{},[1208,1231,1232],{},"Public API \u002F Catalog",[1208,1234,1235],{},"Hourly\u002FDaily",[1208,1237,1238],{},[32,1239,1240],{},"60-120",[1208,1242,1243],{},[32,1244,1245],{},"300-600",[1208,1247,1248],{},"Medium",[1181,1250,1251,1254,1257,1262,1267],{},[1208,1252,1253],{},"User Dashboard \u002F Feeds",[1208,1255,1256],{},"Real-time",[1208,1258,1259],{},[32,1260,1261],{},"0-15",[1208,1263,1264],{},[32,1265,1266],{},"30-60",[1208,1268,1269],{},"High",[1181,1271,1272,1275,1278,1283,1288],{},[1208,1273,1274],{},"Authenticated\u002FTransactional",[1208,1276,1277],{},"Session-bound",[1208,1279,1280,1282],{},[32,1281,64],{}," (no-cache)",[1208,1284,1285,1287],{},[32,1286,64],{}," (disabled)",[1208,1289,1290],{},"Critical",[14,1292,1293,1296,1297,1300,1301,1304,1305,1308],{},[50,1294,1295],{},"Authenticated Endpoints:"," Never apply SWR to routes returning ",[32,1298,1299],{},"Set-Cookie"," or personalized payloads. Use ",[32,1302,1303],{},"private, max-age=0, no-cache"," to force validation. If background fetches leak session tokens, implement strict CORS and ",[32,1306,1307],{},"Vary: Cookie"," headers, though disabling SWR entirely is safer.",[14,1310,1311,1314,1315,1005,1318,1321],{},[50,1312,1313],{},"Predictive Overlap:"," SWR background fetches can be strategically overlapped with user intent. Implementing predictive prefetching for user navigation allows you to trigger background updates on ",[32,1316,1317],{},"mouseenter",[32,1319,1320],{},"pointerdown",", ensuring the next route is already in the stale window before navigation occurs.",[14,1323,1324,1327,1328,1331,1332,1334,1335,1338],{},[50,1325,1326],{},"Emergency Invalidation & Fallbacks:"," When immediate cache purge is required, deploy versioned URLs (",[32,1329,1330],{},"\u002Fstatic\u002Fv2.1.0\u002Fapp.js",") rather than relying on ",[32,1333,38],{}," overrides. For network flakiness during the stale window, implement a fallback routing layer in your Service Worker that serves the stale payload if the background fetch fails within ",[32,1336,1337],{},"3000ms",", preventing UI degradation during transient outages.",[24,1340,1342],{"id":1341},"common-mistakes","Common Mistakes",[1175,1344,1345,1358],{},[1178,1346,1347],{},[1181,1348,1349,1352,1355],{},[1184,1350,1351],{},"Mistake",[1184,1353,1354],{},"Impact",[1184,1356,1357],{},"Fix",[1203,1359,1360,1382,1407,1421],{},[1181,1361,1362,1370,1373],{},[1208,1363,1364,1365,1367,1368],{},"Setting ",[32,1366,34],{}," shorter than ",[32,1369,42],{},[1208,1371,1372],{},"Browsers ignore the directive, causing immediate cache expiration and origin load spikes.",[1208,1374,1375,1376,1378,1379,1381],{},"Ensure ",[32,1377,34],{}," ≥ ",[32,1380,42],{}," to guarantee a valid grace period.",[1181,1383,1384,1390,1401],{},[1208,1385,1386,1387,1389],{},"Ignoring ",[32,1388,296],{}," header implications",[1208,1391,1392,1393,1396,1397,1400],{},"Cache fragmentation across ",[32,1394,1395],{},"Accept-Encoding","\u002F",[32,1398,1399],{},"Accept-Language",", degrading TTFB for 30-50% of users.",[1208,1402,1403,1404,1406],{},"Explicitly declare ",[32,1405,296],{}," headers and verify CDN edge normalization supports them.",[1181,1408,1409,1412,1415],{},[1208,1410,1411],{},"Blocking the main thread during background fetch",[1208,1413,1414],{},"Increased INP and FID due to synchronous cache writes or unhandled promises.",[1208,1416,1417,1418,1420],{},"Use ",[32,1419,738],{}," in SWs and offload heavy cache serialization to Web Workers.",[1181,1422,1423,1426,1429],{},[1208,1424,1425],{},"Over-caching authenticated\u002Fpersonalized endpoints",[1208,1427,1428],{},"Cross-session data leakage, violating security and privacy compliance.",[1208,1430,1431,1432,1435,1436,1439],{},"Restrict SWR to ",[32,1433,1434],{},"public"," routes. Apply ",[32,1437,1438],{},"private, no-cache"," to authenticated paths.",[24,1441,1443],{"id":1442},"faq","FAQ",[14,1445,1446,1456,1458,1459,1461,1462,1465],{},[50,1447,1448,1449,1451,1452,1455],{},"How does ",[32,1450,34],{}," differ from ",[32,1453,1454],{},"stale-if-error","?",[32,1457,34],{}," serves cached content while proactively fetching an update during normal operation. ",[32,1460,1454],{}," only serves stale content reactively when the origin returns a ",[32,1463,1464],{},"5xx"," error or network failure. They are complementary: SWR optimizes latency, while stale-if-error provides fault tolerance.",[14,1467,1468,1471,1472,1474,1475,1477],{},[50,1469,1470],{},"Can I use SWR for GraphQL queries?","\nYes, but HTTP caching semantics require ",[32,1473,83],{}," requests. Configure your GraphQL client to use persisted queries or GET-based endpoints, then apply SWR at the CDN\u002Forigin level. Avoid POST caching unless your CDN explicitly supports ",[32,1476,38],{}," normalization for GraphQL operations.",[14,1479,1480,1486,1487,1490,1491,1494,1495,1498],{},[50,1481,1482,1483,1485],{},"What is the recommended ",[32,1484,34],{}," duration for dynamic API responses?","\nFor dynamic APIs, ",[32,1488,1489],{},"max-age=60-120s"," with ",[32,1492,1493],{},"stale-while-revalidate=300-600s"," is optimal. This balances freshness with background revalidation, keeping TTFB under ",[32,1496,1497],{},"150ms"," while ensuring data updates within 10 minutes. Adjust downward for high-frequency trading or real-time dashboards.",[14,1500,1501,1504,1505,1508,1509,1005,1512,1515,1516,1518,1519,1522],{},[50,1502,1503],{},"How do I verify that background revalidation is actually occurring?","\nUse Chrome DevTools Network tab with ",[32,1506,1507],{},"Disable cache"," unchecked. Look for ",[32,1510,1511],{},"Status: 200 (from disk cache)",[32,1513,1514],{},"from service worker",". In WebPageTest, inspect the waterfall for concurrent background requests marked as ",[32,1517,87],{}," priority. In RUM, track the delta between cache hit timestamps and subsequent ",[32,1520,1521],{},"Last-Modified"," header changes to confirm successful revalidation cycles.",[1524,1525,1526],"style",{},"html pre.shiki code .sCJTb, html code.shiki .sCJTb{--shiki-default:#FF9492;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .s3sCt, html code.shiki .s3sCt{--shiki-default:#F0F3F6;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .spFnL, html code.shiki .spFnL{--shiki-default:#FFB757;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .s5hCx, html code.shiki .s5hCx{--shiki-default:#91CBFF;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sJdzJ, html code.shiki .sJdzJ{--shiki-default:#ADDCFF;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sXJMR, html code.shiki .sXJMR{--shiki-default:#BDC4CC;--shiki-dark:#BDC4CC;--shiki-light:#66707B}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 .sGhOu, html code.shiki .sGhOu{--shiki-default:#DBB7FF;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}",{"title":161,"searchDepth":186,"depth":186,"links":1528},[1529,1530,1535,1536,1537,1538,1539],{"id":26,"depth":186,"text":27},{"id":136,"depth":186,"text":137,"children":1531},[1532,1533,1534],{"id":150,"depth":200,"text":151},{"id":303,"depth":200,"text":304},{"id":518,"depth":200,"text":519},{"id":727,"depth":186,"text":728},{"id":1030,"depth":186,"text":1031},{"id":1161,"depth":186,"text":1162},{"id":1341,"depth":186,"text":1342},{"id":1442,"depth":186,"text":1443},"Stale-While-Revalidate (SWR) is a critical HTTP caching directive that decouples response latency from data freshness, enabling instant delivery of cached assets while silently updating them in the background. This guide provides a production-ready blueprint for implementing SWR across HTTP headers, CDN edge layers, and client-side service workers. While foundational caching architecture is documented in Advanced Caching Strategies & CDN Architecture, this article focuses exclusively on configuration syntax, diagnostic workflows, and explicit metric thresholds required to optimize Core Web Vitals without introducing cache-stampede risks or stale-content penalties.","md",{},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation",{"title":5,"description":1540},"advanced-caching-strategies-cdn-architecture\u002Fstale-while-revalidate-implementation\u002Findex","8NsVldFI9Z7S9UmwcVTee2BNPB2XJi93Si5pYkEPHVE",[1548,1552],{"title":1549,"path":1550,"stem":1551,"children":-1},"Debugging Service Worker Cache Misses in Production","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fdebugging-service-worker-cache-misses-in-production","advanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fdebugging-service-worker-cache-misses-in-production\u002Findex",{"title":1553,"path":1554,"stem":1555,"children":-1},"Core Web Vitals & Measurement: Metric-Driven Architecture for Production","\u002Fcore-web-vitals-measurement","core-web-vitals-measurement\u002Findex",1777925998244]