[{"data":1,"prerenderedAt":1127},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy":1118},{"id":4,"title":5,"body":6,"description":1098,"extension":1099,"meta":1100,"navigation":1112,"path":1113,"seo":1114,"stem":1116,"__hash__":1117},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy\u002Findex.md","Invalidating Service Worker Caches on Deploy",{"type":7,"value":8,"toc":1081},"minimark",[9,14,34,37,167,172,217,221,227,233,239,245,359,363,368,528,532,705,708,712,723,727,739,868,872,875,879,886,890,897,901,931,935,945,954,960,966,970,982,994,1003,1016,1032,1041,1045,1066,1071,1074,1077],[10,11,13],"h1",{"id":12},"how-to-invalidate-service-worker-caches-on-deploy","How to Invalidate Service Worker Caches on Deploy",[15,16,17,18,23,24,28,29,33],"p",{},"This guide covers the cache layer that purges cannot reach, within ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002F","Cache Invalidation Patterns"," in ",[19,25,27],{"href":26},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",". A service worker's Cache Storage is controlled entirely by your service worker code. It ignores ",[30,31,32],"code",{},"Cache-Control",", cannot be purged by the CDN, and persists across visits. If the service worker serves the app shell from cache, a deploy does nothing for returning users until the service worker itself updates — and a careless update can delete caches still in use by open tabs, causing broken pages.",[15,35,36],{},"Done well, service worker caching makes repeat visits nearly instant and keeps the site usable offline, while deploys roll out promptly. The mechanisms are cache versioning (each release writes to new cache names), cleanup on activation (old caches are deleted only when the new worker takes control), and an update flow that decides when open tabs switch to the new version.",[15,38,39],{},[40,41,47,48,47,55,47,59,47,62,47,71,47,77,47,82,47,90,47,97,47,101,47,108,47,113,47,120,47,124,47,128,47,134,47,140,47,143,47,146,47,150,47,154,47,158,47,162,47],"svg",{"viewBox":42,"width":43,"role":44,"ariaLabel":45,"style":46},"0 0 760 204","100%","img","Timeline of a deploy where the new service worker installs and precaches in the background, waits, then activates and cleans up old caches.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[49,50],"rect",{"className":51,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],"svg-canvas","0","#ffffff",[56,57,58],"title",{},"Service worker lifecycle across a deploy",[60,61,45],"desc",{},[49,63],{"x":64,"y":64,"width":65,"height":66,"rx":67,"fill":68,"stroke":69,"style":70},"1","758","202","10","none","currentColor","stroke-opacity:0.18",[72,73,58],"text",{"x":74,"y":75,"fill":69,"style":76},"28.0","34.0","font-size:16px;font-weight:700",[72,78,81],{"x":74,"y":79,"fill":69,"style":80},"74.0","font-size:12.5px;font-weight:700","Old worker",[49,83],{"x":84,"y":85,"width":86,"height":87,"rx":88,"fill":69,"stroke":69,"style":89},"112.2","56.0","408.2","26.0","3","fill-opacity:0.06;stroke-opacity:0.4",[72,91,96],{"x":92,"y":93,"fill":69,"style":94,"textAnchor":95},"316.3","73.5","font-size:11.5px","middle","controls open tabs",[72,98,100],{"x":74,"y":99,"fill":69,"style":80},"112.0","New worker",[49,102],{"x":103,"y":104,"width":105,"height":87,"rx":88,"fill":106,"stroke":106,"style":107},"163.4","94.0","100.9","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[72,109,112],{"x":110,"y":111,"fill":69,"style":94,"textAnchor":95},"213.9","111.5","install + precache",[49,114],{"x":115,"y":104,"width":116,"height":87,"rx":88,"fill":117,"stroke":118,"style":119},"265.8","254.6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[72,121,123],{"x":122,"y":111,"fill":69,"style":94,"textAnchor":95},"393.1","waiting",[49,125],{"x":126,"y":104,"width":127,"height":87,"rx":88,"fill":106,"stroke":106,"style":107},"521.9","60.0",[129,130],"line",{"x1":111,"y1":131,"x2":132,"y2":131,"stroke":69,"style":133},"134.0","726.0","stroke-opacity:0.4",[72,135,139],{"x":136,"y":137,"fill":138,"style":94,"textAnchor":95},"117.4","151.0","#51617a","0s",[72,141,142],{"x":110,"y":137,"fill":138,"style":94,"textAnchor":95},"10s",[72,144,145],{"x":92,"y":137,"fill":138,"style":94,"textAnchor":95},"20s",[72,147,149],{"x":148,"y":137,"fill":138,"style":94,"textAnchor":95},"418.7","30s",[72,151,153],{"x":152,"y":137,"fill":138,"style":94,"textAnchor":95},"521.2","40s",[72,155,157],{"x":156,"y":137,"fill":138,"style":94,"textAnchor":95},"623.6","50s",[72,159,161],{"x":160,"y":137,"fill":138,"style":94,"textAnchor":95},"723.0","60s",[72,163,166],{"x":74,"y":164,"fill":138,"style":165},"182.0","font-size:12.5px","The new worker activates when old tabs close (or when skipWaiting is used); only then are old caches deleted.",[168,169,171],"h2",{"id":170},"rapid-diagnosis","Rapid Diagnosis",[173,174,175,183,197,203],"ul",{},[176,177,178,182],"li",{},[179,180,181],"strong",{},"Open DevTools → Application → Service Workers."," Check whether a new worker is \"waiting to activate\" after a deploy.",[176,184,185,188,189,192,193,196],{},[179,186,187],{},"Inspect Cache Storage."," Look for cache names without versions (",[30,190,191],{},"static",", ",[30,194,195],{},"pages",") that never change, or many accumulated old versions.",[176,198,199,202],{},[179,200,201],{},"Check what is cached."," If HTML is served cache-first from the service worker, users see old releases until the worker updates.",[176,204,205,208,209,212,213,216],{},[179,206,207],{},"Check the service worker script's HTTP caching."," The ",[30,210,211],{},"sw.js"," file itself should be served with ",[30,214,215],{},"Cache-Control: no-cache"," (browsers also cap its HTTP cache lifetime) so updates are detected.",[168,218,220],{"id":219},"root-cause-analysis","Root Cause Analysis",[15,222,223,226],{},[179,224,225],{},"1. Unversioned cache names."," New assets are added to the same cache as old ones; old assets accumulate and stale HTML persists.",[15,228,229,232],{},[179,230,231],{},"2. Cache-first HTML."," The shell is served from cache indefinitely; the network version is never used while the old worker controls the page.",[15,234,235,238],{},[179,236,237],{},"3. Workers stuck waiting."," New workers wait until all tabs controlled by the old one close; long-lived tabs keep old versions alive for days.",[15,240,241,244],{},[179,242,243],{},"4. Over-eager cleanup."," Deleting old caches during install (instead of activate) breaks tabs still running the old version that request lazily loaded chunks.",[15,246,247],{},[40,248,47,251,47,254,47,257,47,259,47,262,47,264,47,268,47,274,47,278,47,282,47,285,47,289,47,293,47,297,47,299,47,303,47,305,47,308,47,311,47,315,47,317,47,320,47,322,47,325,47,328,47,332,47,334,47,337,47,339,47,342,47,345,47,349,47,351,47,354,47,356,47],{"viewBox":249,"width":43,"role":44,"ariaLabel":250,"style":46},"0 0 760 228","Recommended service worker caching strategies for HTML, hashed assets, data and images, with their invalidation behaviour.",[49,252],{"className":253,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],[56,255,256],{},"Cache strategies by resource type in a service worker",[60,258,250],{},[49,260],{"x":64,"y":64,"width":65,"height":261,"rx":67,"fill":68,"stroke":69,"style":70},"226",[72,263,256],{"x":74,"y":75,"fill":69,"style":76},[49,265],{"x":74,"y":85,"width":266,"height":267,"rx":53,"fill":69,"stroke":69,"style":89},"120.2","30.0",[72,269,273],{"x":270,"y":271,"fill":69,"style":80,"textAnchor":272},"38.0","75.5","start","Resource",[49,275],{"x":276,"y":85,"width":277,"height":267,"rx":53,"fill":69,"stroke":69,"style":89},"148.2","291.9",[72,279,281],{"x":280,"y":271,"fill":69,"style":80,"textAnchor":95},"294.1","Strategy",[49,283],{"x":284,"y":85,"width":277,"height":267,"rx":53,"fill":69,"stroke":69,"style":89},"440.1",[72,286,288],{"x":287,"y":271,"fill":69,"style":80,"textAnchor":95},"586.0","Invalidation",[49,290],{"x":74,"y":291,"width":266,"height":267,"rx":53,"fill":68,"stroke":69,"style":292},"86.0","stroke-opacity:0.35",[72,294,296],{"x":270,"y":295,"fill":69,"style":80,"textAnchor":272},"105.5","HTML \u002F app shell",[49,298],{"x":276,"y":291,"width":277,"height":267,"rx":53,"fill":106,"stroke":106,"style":107},[72,300,302],{"x":280,"y":295,"fill":69,"style":301,"textAnchor":95},"font-size:12px","network-first or SWR",[49,304],{"x":284,"y":291,"width":277,"height":267,"rx":53,"fill":106,"stroke":106,"style":107},[72,306,307],{"x":287,"y":295,"fill":69,"style":301,"textAnchor":95},"updates on next visit",[49,309],{"x":74,"y":310,"width":266,"height":267,"rx":53,"fill":68,"stroke":69,"style":292},"116.0",[72,312,314],{"x":270,"y":313,"fill":69,"style":80,"textAnchor":272},"135.5","Hashed JS\u002FCSS",[49,316],{"x":276,"y":310,"width":277,"height":267,"rx":53,"fill":106,"stroke":106,"style":107},[72,318,319],{"x":280,"y":313,"fill":69,"style":301,"textAnchor":95},"precache, cache-first",[49,321],{"x":284,"y":310,"width":277,"height":267,"rx":53,"fill":106,"stroke":106,"style":107},[72,323,324],{"x":287,"y":313,"fill":69,"style":301,"textAnchor":95},"new hashes per release",[49,326],{"x":74,"y":327,"width":266,"height":267,"rx":53,"fill":68,"stroke":69,"style":292},"146.0",[72,329,331],{"x":270,"y":330,"fill":69,"style":80,"textAnchor":272},"165.5","API data",[49,333],{"x":276,"y":327,"width":277,"height":267,"rx":53,"fill":69,"stroke":69,"style":89},[72,335,336],{"x":280,"y":330,"fill":69,"style":301,"textAnchor":95},"network-first with fallback",[49,338],{"x":284,"y":327,"width":277,"height":267,"rx":53,"fill":106,"stroke":106,"style":107},[72,340,341],{"x":287,"y":330,"fill":69,"style":301,"textAnchor":95},"always tries fresh",[49,343],{"x":74,"y":344,"width":266,"height":267,"rx":53,"fill":68,"stroke":69,"style":292},"176.0",[72,346,348],{"x":270,"y":347,"fill":69,"style":80,"textAnchor":272},"195.5","Images",[49,350],{"x":276,"y":344,"width":277,"height":267,"rx":53,"fill":69,"stroke":69,"style":89},[72,352,353],{"x":280,"y":347,"fill":69,"style":301,"textAnchor":95},"cache-first with expiry",[49,355],{"x":284,"y":344,"width":277,"height":267,"rx":53,"fill":69,"stroke":69,"style":89},[72,357,358],{"x":287,"y":347,"fill":69,"style":301,"textAnchor":95},"LRU \u002F max entries",[168,360,362],{"id":361},"step-by-step-resolution","Step-by-Step Resolution",[364,365,367],"h3",{"id":366},"_1-version-cache-names-with-the-release","1. Version cache names with the release",[369,370,375],"pre",{"className":371,"code":372,"language":373,"meta":374,"style":374},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F sw.js — BUILD_ID is injected at build time.\nconst VERSION = BUILD_ID;\nconst PRECACHE = `precache-${VERSION}`;\nself.addEventListener('install', (event) => {\n  event.waitUntil(caches.open(PRECACHE).then((c) => c.addAll(PRECACHE_URLS)));\n});\n\u002F\u002F trade-off: every release downloads the full precache list. Keep it to the\n\u002F\u002F shell and critical assets; let other assets be cached at runtime.\n","javascript","",[30,376,377,385,406,428,460,510,516,522],{"__ignoreMap":374},[378,379,381],"span",{"class":129,"line":380},1,[378,382,384],{"class":383},"sjfSM","\u002F\u002F sw.js — BUILD_ID is injected at build time.\n",[378,386,388,392,396,399,402],{"class":129,"line":387},2,[378,389,391],{"class":390},"sPARh","const",[378,393,395],{"class":394},"sPXB4"," VERSION",[378,397,398],{"class":390}," =",[378,400,401],{"class":394}," BUILD_ID",[378,403,405],{"class":404},"saISM",";\n",[378,407,409,411,414,416,420,423,426],{"class":129,"line":408},3,[378,410,391],{"class":390},[378,412,413],{"class":394}," PRECACHE",[378,415,398],{"class":390},[378,417,419],{"class":418},"sZ8jY"," `precache-${",[378,421,422],{"class":394},"VERSION",[378,424,425],{"class":418},"}`",[378,427,405],{"class":404},[378,429,431,434,438,441,444,447,451,454,457],{"class":129,"line":430},4,[378,432,433],{"class":404},"self.",[378,435,437],{"class":436},"smZ65","addEventListener",[378,439,440],{"class":404},"(",[378,442,443],{"class":418},"'install'",[378,445,446],{"class":404},", (",[378,448,450],{"class":449},"sQw3B","event",[378,452,453],{"class":404},") ",[378,455,456],{"class":390},"=>",[378,458,459],{"class":404}," {\n",[378,461,463,466,469,472,475,477,480,483,486,489,492,494,496,499,502,504,507],{"class":129,"line":462},5,[378,464,465],{"class":404},"  event.",[378,467,468],{"class":436},"waitUntil",[378,470,471],{"class":404},"(caches.",[378,473,474],{"class":436},"open",[378,476,440],{"class":404},[378,478,479],{"class":394},"PRECACHE",[378,481,482],{"class":404},").",[378,484,485],{"class":436},"then",[378,487,488],{"class":404},"((",[378,490,491],{"class":449},"c",[378,493,453],{"class":404},[378,495,456],{"class":390},[378,497,498],{"class":404}," c.",[378,500,501],{"class":436},"addAll",[378,503,440],{"class":404},[378,505,506],{"class":394},"PRECACHE_URLS",[378,508,509],{"class":404},")));\n",[378,511,513],{"class":129,"line":512},6,[378,514,515],{"class":404},"});\n",[378,517,519],{"class":129,"line":518},7,[378,520,521],{"class":383},"\u002F\u002F trade-off: every release downloads the full precache list. Keep it to the\n",[378,523,525],{"class":129,"line":524},8,[378,526,527],{"class":383},"\u002F\u002F shell and critical assets; let other assets be cached at runtime.\n",[364,529,531],{"id":530},"_2-delete-old-caches-only-on-activate","2. Delete old caches only on activate",[369,533,535],{"className":371,"code":534,"language":373,"meta":374,"style":374},"self.addEventListener('activate', (event) => {\n  event.waitUntil((async () => {\n    const keep = new Set([PRECACHE, `runtime-${VERSION}`]);\n    for (const name of await caches.keys()) if (!keep.has(name)) await caches.delete(name);\n    await self.clients.claim();\n  })());\n});\n\u002F\u002F trade-off: claim() makes the new worker control open pages immediately;\n\u002F\u002F those pages still run old JavaScript, which may now fetch through a worker\n\u002F\u002F that no longer has old chunks cached. Keep old hashed assets on the server.\n",[30,536,537,558,576,609,665,679,684,688,693,699],{"__ignoreMap":374},[378,538,539,541,543,545,548,550,552,554,556],{"class":129,"line":380},[378,540,433],{"class":404},[378,542,437],{"class":436},[378,544,440],{"class":404},[378,546,547],{"class":418},"'activate'",[378,549,446],{"class":404},[378,551,450],{"class":449},[378,553,453],{"class":404},[378,555,456],{"class":390},[378,557,459],{"class":404},[378,559,560,562,564,566,569,572,574],{"class":129,"line":387},[378,561,465],{"class":404},[378,563,468],{"class":436},[378,565,488],{"class":404},[378,567,568],{"class":390},"async",[378,570,571],{"class":404}," () ",[378,573,456],{"class":390},[378,575,459],{"class":404},[378,577,578,581,584,586,589,592,595,597,599,602,604,606],{"class":129,"line":408},[378,579,580],{"class":390},"    const",[378,582,583],{"class":394}," keep",[378,585,398],{"class":390},[378,587,588],{"class":390}," new",[378,590,591],{"class":436}," Set",[378,593,594],{"class":404},"([",[378,596,479],{"class":394},[378,598,192],{"class":404},[378,600,601],{"class":418},"`runtime-${",[378,603,422],{"class":394},[378,605,425],{"class":418},[378,607,608],{"class":404},"]);\n",[378,610,611,614,617,619,622,625,628,631,634,637,640,642,645,648,651,654,657,659,662],{"class":129,"line":430},[378,612,613],{"class":390},"    for",[378,615,616],{"class":404}," (",[378,618,391],{"class":390},[378,620,621],{"class":394}," name",[378,623,624],{"class":390}," of",[378,626,627],{"class":390}," await",[378,629,630],{"class":404}," caches.",[378,632,633],{"class":436},"keys",[378,635,636],{"class":404},"()) ",[378,638,639],{"class":390},"if",[378,641,616],{"class":404},[378,643,644],{"class":390},"!",[378,646,647],{"class":404},"keep.",[378,649,650],{"class":436},"has",[378,652,653],{"class":404},"(name)) ",[378,655,656],{"class":390},"await",[378,658,630],{"class":404},[378,660,661],{"class":436},"delete",[378,663,664],{"class":404},"(name);\n",[378,666,667,670,673,676],{"class":129,"line":462},[378,668,669],{"class":390},"    await",[378,671,672],{"class":404}," self.clients.",[378,674,675],{"class":436},"claim",[378,677,678],{"class":404},"();\n",[378,680,681],{"class":129,"line":512},[378,682,683],{"class":404},"  })());\n",[378,685,686],{"class":129,"line":518},[378,687,515],{"class":404},[378,689,690],{"class":129,"line":524},[378,691,692],{"class":383},"\u002F\u002F trade-off: claim() makes the new worker control open pages immediately;\n",[378,694,696],{"class":129,"line":695},9,[378,697,698],{"class":383},"\u002F\u002F those pages still run old JavaScript, which may now fetch through a worker\n",[378,700,702],{"class":129,"line":701},10,[378,703,704],{"class":383},"\u002F\u002F that no longer has old chunks cached. Keep old hashed assets on the server.\n",[15,706,707],{},"Expected outcome: caches never mix releases, and nothing is deleted while the old worker is still in charge.",[364,709,711],{"id":710},"_3-serve-html-network-first-or-stale-while-revalidate","3. Serve HTML network-first (or stale-while-revalidate)",[15,713,714,715,718,719,722],{},"Navigations should try the network first (with a timeout and cache fallback for offline), so a deploy is visible on the next page load even before the new worker activates. Workbox's ",[30,716,717],{},"NetworkFirst"," with ",[30,720,721],{},"networkTimeoutSeconds"," is a ready-made implementation.",[364,724,726],{"id":725},"_4-decide-the-update-ux","4. Decide the update UX",[15,728,729,730,733,734,738],{},"Either prompt users (\"A new version is available — refresh\") and call ",[30,731,732],{},"skipWaiting"," on confirmation, or skip waiting automatically and reload at a safe moment. ",[19,735,737],{"href":736},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fhandling-service-worker-updates-with-skipwaiting\u002F","Handling service worker updates with skipWaiting"," compares the approaches.",[15,740,741],{},[40,742,47,745,47,748,47,751,47,753,47,756,47,758,47,764,47,769,47,772,47,775,47,779,47,783,47,785,47,789,47,793,47,796,47,800,47,804,47,807,47,811,47,815,47,822,47,826,47,830,47,834,47,839,47,841,47,844,47,848,47,851,47,854,47,857,47,861,47,864,47],{"viewBox":743,"width":43,"role":44,"ariaLabel":744,"style":46},"0 0 760 381","Five practices that ensure service worker caches update cleanly on deploy.",[49,746],{"className":747,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],[56,749,750],{},"Deploy-safe service worker checklist",[60,752,744],{},[49,754],{"x":64,"y":64,"width":65,"height":755,"rx":67,"fill":68,"stroke":69,"style":70},"379",[72,757,750],{"x":74,"y":75,"fill":69,"style":76},[49,759],{"x":760,"y":85,"width":761,"height":762,"rx":763,"fill":106,"stroke":106,"style":107},"72.0","660.0","51.0","6",[72,765,768],{"x":291,"y":766,"fill":69,"style":767,"textAnchor":272},"77.0","font-size:13px;font-weight:700","Serve sw.js with no-cache",[72,770,771],{"x":291,"y":104,"fill":69,"style":301,"textAnchor":272},"So browsers detect the new worker promptly",[49,773],{"x":760,"y":774,"width":761,"height":762,"rx":763,"fill":106,"stroke":106,"style":107},"119.0",[72,776,778],{"x":291,"y":777,"fill":69,"style":767,"textAnchor":272},"140.0","Versioned cache names",[72,780,782],{"x":291,"y":781,"fill":69,"style":301,"textAnchor":272},"157.0","Each release writes to its own caches",[49,784],{"x":760,"y":164,"width":761,"height":762,"rx":763,"fill":106,"stroke":106,"style":107},[72,786,788],{"x":291,"y":787,"fill":69,"style":767,"textAnchor":272},"203.0","Cleanup on activate",[72,790,792],{"x":291,"y":791,"fill":69,"style":301,"textAnchor":272},"220.0","Never during install",[49,794],{"x":760,"y":795,"width":761,"height":762,"rx":763,"fill":106,"stroke":106,"style":107},"245.0",[72,797,799],{"x":291,"y":798,"fill":69,"style":767,"textAnchor":272},"266.0","Network-first navigations",[72,801,803],{"x":291,"y":802,"fill":69,"style":301,"textAnchor":272},"283.0","New HTML visible on next load",[49,805],{"x":760,"y":806,"width":761,"height":762,"rx":763,"fill":106,"stroke":106,"style":107},"308.0",[72,808,810],{"x":291,"y":809,"fill":69,"style":767,"textAnchor":272},"329.0","Keep old hashed assets on the server",[72,812,814],{"x":291,"y":813,"fill":69,"style":301,"textAnchor":272},"346.0","Old tabs can still lazy-load chunks",[129,816],{"x1":817,"y1":818,"x2":817,"y2":819,"stroke":69,"strokeWidth":820,"style":821},"43.0","95.5","130.5","1.5","stroke-opacity:0.3",[129,823],{"x1":817,"y1":824,"x2":817,"y2":825,"stroke":69,"strokeWidth":820,"style":821},"158.5","193.5",[129,827],{"x1":817,"y1":828,"x2":817,"y2":829,"stroke":69,"strokeWidth":820,"style":821},"221.5","256.5",[129,831],{"x1":817,"y1":832,"x2":817,"y2":833,"stroke":69,"strokeWidth":820,"style":821},"284.5","319.5",[835,836],"circle",{"cx":817,"cy":837,"r":838,"fill":106},"81.5","13",[72,840,64],{"x":817,"y":291,"fill":54,"style":767,"textAnchor":95},[835,842],{"cx":817,"cy":843,"r":838,"fill":106},"144.5",[72,845,847],{"x":817,"y":846,"fill":54,"style":767,"textAnchor":95},"149.0","2",[835,849],{"cx":817,"cy":850,"r":838,"fill":106},"207.5",[72,852,88],{"x":817,"y":853,"fill":54,"style":767,"textAnchor":95},"212.0",[835,855],{"cx":817,"cy":856,"r":838,"fill":106},"270.5",[72,858,860],{"x":817,"y":859,"fill":54,"style":767,"textAnchor":95},"275.0","4",[835,862],{"cx":817,"cy":863,"r":838,"fill":106},"333.5",[72,865,867],{"x":817,"y":866,"fill":54,"style":767,"textAnchor":95},"338.0","5",[168,869,871],{"id":870},"verification","Verification",[15,873,874],{},"Deploy to staging with a tab open. Confirm the new worker installs, waits (or activates per your policy), and that after activation Cache Storage contains only the new version's caches. Load a page in a new tab and confirm it shows the new release. Check that the old tab can still navigate without chunk errors.",[168,876,878],{"id":877},"worked-example-a-pwa-stuck-on-old-releases","Worked Example: A PWA Stuck on Old Releases",[15,880,881,882,885],{},"A progressive web app served its shell cache-first from an unversioned ",[30,883,884],{},"app-shell"," cache and never deleted old entries. Users who installed it saw releases weeks late, and storage usage grew past 300MB on some devices. The team versioned caches by build ID, switched navigations to network-first with a three-second timeout, cleaned up on activate, and showed a refresh prompt when a new worker was waiting. New releases reached 90% of active users within a day, and storage fell to under 20MB per user.",[168,887,889],{"id":888},"measuring-update-propagation","Measuring Update Propagation",[15,891,892,893,896],{},"Know how quickly releases reach users. Include the build ID in RUM beacons (read it from the page or from the controlling service worker via ",[30,894,895],{},"postMessage",") and chart the share of sessions on the latest build in the days after each deploy. A healthy service worker setup reaches most active users within a day; a long tail of sessions on builds weeks old means workers are stuck waiting or HTML is served cache-first. The same data tells you how long to keep old hashed assets on the server — keep them at least as long as meaningful traffic remains on old builds.",[168,898,900],{"id":899},"common-mistakes","Common Mistakes",[173,902,903,913,919,925],{},[176,904,905,908,909,912],{},[179,906,907],{},"Caching sw.js with a long max-age."," Browsers re-check service worker scripts at least daily, but long caching delays update detection; use ",[30,910,911],{},"no-cache",".",[176,914,915,918],{},[179,916,917],{},"Deleting caches in install."," Breaks old tabs mid-session.",[176,920,921,924],{},[179,922,923],{},"Precaching everything."," Large precache lists waste bandwidth on every release.",[176,926,927,930],{},[179,928,929],{},"Never testing the update path."," Most service worker bugs appear only during upgrades.",[168,932,934],{"id":933},"edge-cases","Edge Cases",[15,936,937,940,941,912],{},[179,938,939],{},"Navigation preload."," With network-first navigations, enabling navigation preload avoids the worker startup delay on each navigation — see ",[19,942,944],{"href":943},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fenabling-navigation-preload-in-service-workers\u002F","enabling navigation preload in service workers",[15,946,947,950,951,953],{},[179,948,949],{},"Emergency kill switch."," Ship a way to unregister the worker remotely (a \"kill switch\" ",[30,952,211],{}," that unregisters itself and clears caches) in case a broken worker ships.",[15,955,956,959],{},[179,957,958],{},"Opaque responses."," Cross-origin no-CORS responses cached by the worker have padded sizes and count heavily against quota.",[15,961,962,965],{},[179,963,964],{},"Multiple apps on one origin."," Scope workers and cache names per app so one app's cleanup does not delete another's caches.",[168,967,969],{"id":968},"faq","FAQ",[971,972,975,979],"details",{"className":973},[974],"faq-item",[976,977,978],"summary",{},"Does a CDN purge affect service worker caches?",[15,980,981],{},"No. Purges clear CDN caches only. The service worker keeps serving what it has stored until your code updates or deletes it.",[971,983,985,988],{"className":984},[974],[976,986,987],{},"How often do browsers check for a new service worker?",[15,989,990,991,993],{},"On navigations within scope and on certain events, with the worker script's HTTP cache bypassed after 24 hours at most. Serving it with ",[30,992,911],{}," ensures checks see changes immediately.",[971,995,997,1000],{"className":996},[974],[976,998,999],{},"Should I use Workbox?",[15,1001,1002],{},"For most sites, yes. Workbox's precaching handles revisioning and cleanup correctly, and its strategies implement the patterns above. Custom workers are fine but easy to get subtly wrong.",[971,1004,1006,1009],{"className":1005},[974],[976,1007,1008],{},"What about users who never close their tabs?",[15,1010,1011,1012,1015],{},"Use an update prompt or periodic checks (",[30,1013,1014],{},"registration.update()","), and design the app so that an old version keeps working against the current API for a reasonable period.",[971,1017,1019,1022],{"className":1018},[974],[976,1020,1021],{},"Can I force all clients onto a new version immediately?",[15,1023,1024,1025,1027,1028,1031],{},"Calling ",[30,1026,732],{}," and ",[30,1029,1030],{},"clients.claim"," and then reloading clients forces it, at the risk of interrupting users mid-task. Reserve forced updates for critical fixes.",[971,1033,1035,1038],{"className":1034},[974],[976,1036,1037],{},"How do I clean up storage from very old versions?",[15,1039,1040],{},"The activate handler deletes any cache not in the current keep-list, which removes all older versions at once, regardless of how many accumulated.",[168,1042,1044],{"id":1043},"related","Related",[173,1046,1047,1052,1059],{},[176,1048,1049,1051],{},[19,1050,737],{"href":736}," — the update UX.",[176,1053,1054,1058],{},[19,1055,1057],{"href":1056},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fprecaching-vs-runtime-caching-with-workbox\u002F","Precaching vs runtime caching with Workbox"," — what to precache at all.",[176,1060,1061,1065],{},[19,1062,1064],{"href":1063},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fdynamic-imports-and-route-based-splitting\u002Fhandling-chunk-load-errors-after-a-deploy\u002F","Handling chunk load errors after a deploy"," — the related open-tab problem without a service worker.",[1067,1068,1070],"script",{"type":1069},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Invalidate Service Worker Caches on Deploy\",\n  \"description\": \"How to make sure a deploy reaches users whose service worker has cached the previous release, without breaking open tabs or wasting bandwidth.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Version cache names with the release\",\n      \"text\": \"Version cache names with the release\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Delete old caches only on activate\",\n      \"text\": \"Expected outcome: caches never mix releases, and nothing is deleted while the old worker is still in charge.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Serve HTML network-first (or stale-while-revalidate)\",\n      \"text\": \"Navigations should try the network first (with a timeout and cache fallback for offline), so a deploy is visible on the next page load even before the new worker activates.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Decide the update UX\",\n      \"text\": \"Either prompt users (\\\"A new version is available — refresh\\\") and call skipWaiting on confirmation, or skip waiting automatically and reload at a safe moment.\"\n    }\n  ]\n}\n",[1067,1072,1073],{"type":1069},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Invalidate Service Worker Caches on Deploy\",\n  \"description\": \"How to make sure a deploy reaches users whose service worker has cached the previous release, without breaking open tabs or wasting bandwidth.\",\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\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy\u002F\"\n  }\n}\n",[1067,1075,1076],{"type":1069},"\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\": \"Cache Invalidation Patterns\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Invalidating Service Worker Caches on Deploy\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy\u002F\"\n    }\n  ]\n}\n",[1078,1079,1080],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}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":374,"searchDepth":387,"depth":387,"links":1082},[1083,1084,1085,1091,1092,1093,1094,1095,1096,1097],{"id":170,"depth":387,"text":171},{"id":219,"depth":387,"text":220},{"id":361,"depth":387,"text":362,"children":1086},[1087,1088,1089,1090],{"id":366,"depth":408,"text":367},{"id":530,"depth":408,"text":531},{"id":710,"depth":408,"text":711},{"id":725,"depth":408,"text":726},{"id":870,"depth":387,"text":871},{"id":877,"depth":387,"text":878},{"id":888,"depth":387,"text":889},{"id":899,"depth":387,"text":900},{"id":933,"depth":387,"text":934},{"id":968,"depth":387,"text":969},{"id":1043,"depth":387,"text":1044},"How to make sure a deploy reaches users whose service worker has cached the previous release, without breaking open tabs or wasting bandwidth.","md",{"slug":1101,"type":1102,"breadcrumb":1103,"datePublished":1111,"dateModified":1111},"invalidating-service-worker-caches-on-deploy","article",[1104,1107,1108,1109],{"name":1105,"url":1106},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1110},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy\u002F","2026-10-06",true,"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy",{"title":5,"description":1115},"Service worker caches ignore HTTP headers and CDN purges. Version cache names, clean up on activate, and update precaches safely so deploys reach every user.","advanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy\u002Findex","xfuFG5bYwoymJeZn7GiZ72BRpLvsplesMnJ3YZ3nMr4",[1119,1123],{"title":1120,"path":1121,"stem":1122},"Invalidating immutable hashed assets safely","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-immutable-hashed-assets-safely","advanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-immutable-hashed-assets-safely\u002Findex",{"title":1124,"path":1125,"stem":1126},"Purging CDN cache by tag on deploy","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Fpurging-cdn-cache-by-tag-on-deploy","advanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Fpurging-cdn-cache-by-tag-on-deploy\u002Findex",1791308075330]