[{"data":1,"prerenderedAt":1088},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox":1079},{"id":4,"title":5,"body":6,"description":1060,"extension":1061,"meta":1062,"navigation":498,"path":1074,"seo":1075,"stem":1077,"__hash__":1078},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox\u002Findex.md","Offline Fallback Pages with Workbox",{"type":7,"value":8,"toc":1042},"minimark",[9,14,29,32,177,182,211,215,221,227,233,239,299,303,308,437,441,572,576,695,699,710,825,829,835,839,845,849,865,869,872,876,902,906,912,918,924,930,934,946,955,967,976,991,1000,1004,1027,1032,1035,1038],[10,11,13],"h1",{"id":12},"how-to-add-offline-fallback-pages-with-workbox","How to Add Offline Fallback Pages with Workbox",[15,16,17,18,23,24,28],"p",{},"This guide covers the resilience side of ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002F","Service Worker Caching Strategies",", within ",[19,25,27],{"href":26},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",". Without a service worker, a user who loses connectivity mid-browse sees the browser's error page — a dead end that usually ends the session. With a fallback, they see your branded offline page with cached content, a retry button and links to pages already available offline.",[15,30,31],{},"The performance dimension is in the details: how the fallback is triggered. A naive network-first strategy waits for the network request to fail before showing the fallback, which on a flaky connection can mean waiting 30 seconds or more. A strategy with a network timeout shows the fallback (or cached content) after a few seconds instead. And the fallback must be precached and tiny, so installing it costs almost nothing for the majority who never go offline.",[15,33,34],{},[35,36,42,43,42,50,42,54,42,57,42,75,42,83,42,89,42,97,42,104,42,109,42,115,42,119,42,122,42,125,42,129,42,132,42,135,42,139,42,142,42,146,42,149,42,153,42,156,42,165,42,169,42,173,42],"svg",{"viewBox":37,"width":38,"role":39,"ariaLabel":40,"style":41},"0 0 760 149","100%","img","Flow of a navigation request through a network-first strategy with a timeout, falling back to cache and then to the precached offline page.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[44,45],"rect",{"className":46,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],"svg-canvas","0","#ffffff",[51,52,53],"title",{},"Navigation with an offline fallback",[55,56,40],"desc",{},[58,59,60],"defs",{},[61,62,69],"marker",{"id":63,"viewBox":64,"refX":65,"refY":66,"markerWidth":67,"markerHeight":67,"orient":68},"fa9855d7d5","0 0 10 10","9","5","7","auto-start-reverse",[70,71],"path",{"d":72,"fill":73,"style":74},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[44,76],{"x":77,"y":77,"width":78,"height":79,"rx":80,"fill":81,"stroke":73,"style":82},"1","758","147","10","none","stroke-opacity:0.18",[84,85,53],"text",{"x":86,"y":87,"fill":73,"style":88},"28.0","34.0","font-size:16px;font-weight:700",[44,90],{"x":86,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"56.0","108.8","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[84,98,103],{"x":99,"y":100,"fill":73,"style":101,"textAnchor":102},"82.4","78.0","font-size:13px;font-weight:700","middle","Navigation",[84,105,108],{"x":99,"y":106,"fill":73,"style":107,"textAnchor":102},"96.0","font-size:12px","user taps a link",[44,110],{"x":111,"y":91,"width":92,"height":93,"rx":94,"fill":112,"stroke":113,"style":114},"176.8","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[84,116,118],{"x":117,"y":100,"fill":73,"style":101,"textAnchor":102},"231.2","Network first",[84,120,121],{"x":117,"y":106,"fill":73,"style":107,"textAnchor":102},"timeout 3s",[44,123],{"x":124,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"325.6",[84,126,128],{"x":127,"y":100,"fill":73,"style":101,"textAnchor":102},"380.0","Cached page?",[84,130,131],{"x":127,"y":106,"fill":73,"style":107,"textAnchor":102},"if network fails",[44,133],{"x":134,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"474.4",[84,136,138],{"x":137,"y":100,"fill":73,"style":101,"textAnchor":102},"528.8","Offline page",[84,140,141],{"x":137,"y":106,"fill":73,"style":107,"textAnchor":102},"precached",[84,143,145],{"x":137,"y":144,"fill":73,"style":107,"textAnchor":102},"112.0","fallback",[44,147],{"x":148,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"623.2",[84,150,152],{"x":151,"y":100,"fill":73,"style":101,"textAnchor":102},"677.6","Retry",[84,154,155],{"x":151,"y":106,"fill":73,"style":107,"textAnchor":102},"on reconnect",[157,158],"line",{"x1":159,"y1":160,"x2":161,"y2":160,"stroke":73,"strokeWidth":162,"style":163,"markerEnd":164},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa9855d7d5)",[157,166],{"x1":167,"y1":160,"x2":168,"y2":160,"stroke":73,"strokeWidth":162,"style":163,"markerEnd":164},"285.6","323.6",[157,170],{"x1":171,"y1":160,"x2":172,"y2":160,"stroke":73,"strokeWidth":162,"style":163,"markerEnd":164},"434.4","472.4",[157,174],{"x1":175,"y1":160,"x2":176,"y2":160,"stroke":73,"strokeWidth":162,"style":163,"markerEnd":164},"583.2","621.2",[178,179,181],"h2",{"id":180},"rapid-diagnosis","Rapid Diagnosis",[183,184,185,193,199,205],"ul",{},[186,187,188,192],"li",{},[189,190,191],"strong",{},"Test offline in DevTools."," Network panel → Offline, then navigate to a page not visited before. Do you see your page or the browser's error?",[186,194,195,198],{},[189,196,197],{},"Test a flaky network."," Use a custom throttling profile with high latency (2000ms+) and observe how long navigation takes to show anything.",[186,200,201,204],{},[189,202,203],{},"Check precache size."," Application → Cache Storage; the precache should be small (tens of kilobytes for a fallback page and its assets).",[186,206,207,210],{},[189,208,209],{},"Check image and font fallbacks."," Offline pages often appear broken because their images or fonts were never cached.",[178,212,214],{"id":213},"root-cause-analysis","Root Cause Analysis",[15,216,217,220],{},[189,218,219],{},"1. No fallback route."," Navigation failures propagate as network errors.",[15,222,223,226],{},[189,224,225],{},"2. Network-first without a timeout."," Requests on flaky connections hang until the browser gives up, which can take a long time.",[15,228,229,232],{},[189,230,231],{},"3. Fallback depends on uncached assets."," The offline page references CSS, fonts or images that are not precached, so it renders unstyled.",[15,234,235,238],{},[189,236,237],{},"4. Over-large precache."," Teams precache entire sections \"for offline\", costing every user bandwidth on every release.",[15,240,241],{},[35,242,42,245,42,248,42,251,42,253,42,256,42,258,42,265,42,272,42,277,42,281,42,284,42,286,42,290,42,295,42],{"viewBox":243,"width":38,"role":39,"ariaLabel":244,"style":41},"0 0 760 163","Bar chart comparing how long a user waits before seeing content when a connection drops, with and without a network timeout and fallback.",[44,246],{"className":247,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],[51,249,250],{},"Time to show something on a dropped connection",[55,252,244],{},[44,254],{"x":77,"y":77,"width":78,"height":255,"rx":80,"fill":81,"stroke":73,"style":82},"161",[84,257,250],{"x":86,"y":87,"fill":73,"style":88},[84,259,264],{"x":260,"y":261,"fill":73,"style":262,"textAnchor":263},"230.5","70.0","font-size:13px","end","No service worker (browser error)",[44,266],{"x":267,"y":91,"width":268,"height":269,"rx":270,"fill":112,"stroke":113,"style":271},"242.5","425.5","19","3","fill-opacity:0.7;stroke-opacity:0.9",[84,273,276],{"x":274,"y":261,"fill":73,"style":275},"674.0","font-size:12px;font-weight:600","21s",[84,278,280],{"x":260,"y":279,"fill":73,"style":262,"textAnchor":263},"101.0","Network-first, no timeout",[44,282],{"x":267,"y":283,"width":268,"height":269,"rx":270,"fill":112,"stroke":113,"style":271},"87.0",[84,285,276],{"x":274,"y":279,"fill":73,"style":275},[84,287,289],{"x":260,"y":288,"fill":73,"style":262,"textAnchor":263},"132.0","Network-first, 3s timeout + fallback",[44,291],{"x":267,"y":292,"width":293,"height":269,"rx":270,"fill":95,"stroke":95,"style":294},"118.0","60.8","fill-opacity:0.55;stroke-opacity:0.9",[84,296,298],{"x":297,"y":288,"fill":73,"style":275},"309.3","3s",[178,300,302],{"id":301},"step-by-step-resolution","Step-by-Step Resolution",[304,305,307],"h3",{"id":306},"_1-precache-a-small-self-contained-offline-page","1. Precache a small, self-contained offline page",[309,310,315],"pre",{"className":311,"code":312,"language":313,"meta":314,"style":314},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F sw.js (Workbox)\nimport { precacheAndRoute } from 'workbox-precaching';\nprecacheAndRoute([\n  { url: '\u002Foffline.html', revision: OFFLINE_REVISION },   \u002F\u002F inline CSS, no web fonts, small SVG logo\n  ...self.__WB_MANIFEST.filter((e) => e.url.startsWith('\u002Fassets\u002Fcore\u002F')),\n]);\n\u002F\u002F trade-off: inlining styles into offline.html duplicates some CSS, but makes\n\u002F\u002F the fallback independent of other caches — the whole point of a fallback.\n","javascript","",[316,317,318,326,347,357,379,419,425,431],"code",{"__ignoreMap":314},[319,320,322],"span",{"class":157,"line":321},1,[319,323,325],{"class":324},"sjfSM","\u002F\u002F sw.js (Workbox)\n",[319,327,329,333,337,340,344],{"class":157,"line":328},2,[319,330,332],{"class":331},"sPARh","import",[319,334,336],{"class":335},"saISM"," { precacheAndRoute } ",[319,338,339],{"class":331},"from",[319,341,343],{"class":342},"sZ8jY"," 'workbox-precaching'",[319,345,346],{"class":335},";\n",[319,348,350,354],{"class":157,"line":349},3,[319,351,353],{"class":352},"smZ65","precacheAndRoute",[319,355,356],{"class":335},"([\n",[319,358,360,363,366,369,373,376],{"class":157,"line":359},4,[319,361,362],{"class":335},"  { url: ",[319,364,365],{"class":342},"'\u002Foffline.html'",[319,367,368],{"class":335},", revision: ",[319,370,372],{"class":371},"sPXB4","OFFLINE_REVISION",[319,374,375],{"class":335}," },   ",[319,377,378],{"class":324},"\u002F\u002F inline CSS, no web fonts, small SVG logo\n",[319,380,382,385,388,391,394,398,401,404,407,410,413,416],{"class":157,"line":381},5,[319,383,384],{"class":331},"  ...",[319,386,387],{"class":335},"self.__WB_MANIFEST.",[319,389,390],{"class":352},"filter",[319,392,393],{"class":335},"((",[319,395,397],{"class":396},"sQw3B","e",[319,399,400],{"class":335},") ",[319,402,403],{"class":331},"=>",[319,405,406],{"class":335}," e.url.",[319,408,409],{"class":352},"startsWith",[319,411,412],{"class":335},"(",[319,414,415],{"class":342},"'\u002Fassets\u002Fcore\u002F'",[319,417,418],{"class":335},")),\n",[319,420,422],{"class":157,"line":421},6,[319,423,424],{"class":335},"]);\n",[319,426,428],{"class":157,"line":427},7,[319,429,430],{"class":324},"\u002F\u002F trade-off: inlining styles into offline.html duplicates some CSS, but makes\n",[319,432,434],{"class":157,"line":433},8,[319,435,436],{"class":324},"\u002F\u002F the fallback independent of other caches — the whole point of a fallback.\n",[304,438,440],{"id":439},"_2-route-navigations-network-first-with-a-timeout","2. Route navigations network-first with a timeout",[309,442,444],{"className":311,"code":443,"language":313,"meta":314,"style":314},"import { registerRoute, setCatchHandler } from 'workbox-routing';\nimport { NetworkFirst } from 'workbox-strategies';\nimport * as navigationPreload from 'workbox-navigation-preload';\n\nnavigationPreload.enable();\nregisterRoute(({ request }) => request.mode === 'navigate',\n  new NetworkFirst({ cacheName: 'pages', networkTimeoutSeconds: 3 }));\n\u002F\u002F trade-off: a 3s timeout serves cached (possibly outdated) pages to users on\n\u002F\u002F very slow but working connections. Raise it for content that must be fresh.\n",[316,445,446,460,474,494,500,511,539,561,566],{"__ignoreMap":314},[319,447,448,450,453,455,458],{"class":157,"line":321},[319,449,332],{"class":331},[319,451,452],{"class":335}," { registerRoute, setCatchHandler } ",[319,454,339],{"class":331},[319,456,457],{"class":342}," 'workbox-routing'",[319,459,346],{"class":335},[319,461,462,464,467,469,472],{"class":157,"line":328},[319,463,332],{"class":331},[319,465,466],{"class":335}," { NetworkFirst } ",[319,468,339],{"class":331},[319,470,471],{"class":342}," 'workbox-strategies'",[319,473,346],{"class":335},[319,475,476,478,481,484,487,489,492],{"class":157,"line":349},[319,477,332],{"class":331},[319,479,480],{"class":371}," *",[319,482,483],{"class":331}," as",[319,485,486],{"class":335}," navigationPreload ",[319,488,339],{"class":331},[319,490,491],{"class":342}," 'workbox-navigation-preload'",[319,493,346],{"class":335},[319,495,496],{"class":157,"line":359},[319,497,499],{"emptyLinePlaceholder":498},true,"\n",[319,501,502,505,508],{"class":157,"line":381},[319,503,504],{"class":335},"navigationPreload.",[319,506,507],{"class":352},"enable",[319,509,510],{"class":335},"();\n",[319,512,513,516,519,522,525,527,530,533,536],{"class":157,"line":421},[319,514,515],{"class":352},"registerRoute",[319,517,518],{"class":335},"(({ ",[319,520,521],{"class":396},"request",[319,523,524],{"class":335}," }) ",[319,526,403],{"class":331},[319,528,529],{"class":335}," request.mode ",[319,531,532],{"class":331},"===",[319,534,535],{"class":342}," 'navigate'",[319,537,538],{"class":335},",\n",[319,540,541,544,547,550,553,556,558],{"class":157,"line":427},[319,542,543],{"class":331},"  new",[319,545,546],{"class":352}," NetworkFirst",[319,548,549],{"class":335},"({ cacheName: ",[319,551,552],{"class":342},"'pages'",[319,554,555],{"class":335},", networkTimeoutSeconds: ",[319,557,270],{"class":371},[319,559,560],{"class":335}," }));\n",[319,562,563],{"class":157,"line":433},[319,564,565],{"class":324},"\u002F\u002F trade-off: a 3s timeout serves cached (possibly outdated) pages to users on\n",[319,567,569],{"class":157,"line":568},9,[319,570,571],{"class":324},"\u002F\u002F very slow but working connections. Raise it for content that must be fresh.\n",[304,573,575],{"id":574},"_3-catch-failures-and-serve-the-fallback","3. Catch failures and serve the fallback",[309,577,579],{"className":311,"code":578,"language":313,"meta":314,"style":314},"setCatchHandler(async ({ request }) => {\n  if (request.destination === 'document') return caches.match('\u002Foffline.html', { ignoreSearch: true });\n  if (request.destination === 'image') return caches.match('\u002Fassets\u002Fcore\u002Fplaceholder.svg');\n  return Response.error();\n});\n\u002F\u002F trade-off: a generic placeholder image can confuse users if many images fail;\n\u002F\u002F keep it neutral and small.\n",[316,580,581,603,640,667,680,685,690],{"__ignoreMap":314},[319,582,583,586,588,591,594,596,598,600],{"class":157,"line":321},[319,584,585],{"class":352},"setCatchHandler",[319,587,412],{"class":335},[319,589,590],{"class":331},"async",[319,592,593],{"class":335}," ({ ",[319,595,521],{"class":396},[319,597,524],{"class":335},[319,599,403],{"class":331},[319,601,602],{"class":335}," {\n",[319,604,605,608,611,613,616,618,621,624,627,629,631,634,637],{"class":157,"line":328},[319,606,607],{"class":331},"  if",[319,609,610],{"class":335}," (request.destination ",[319,612,532],{"class":331},[319,614,615],{"class":342}," 'document'",[319,617,400],{"class":335},[319,619,620],{"class":331},"return",[319,622,623],{"class":335}," caches.",[319,625,626],{"class":352},"match",[319,628,412],{"class":335},[319,630,365],{"class":342},[319,632,633],{"class":335},", { ignoreSearch: ",[319,635,636],{"class":371},"true",[319,638,639],{"class":335}," });\n",[319,641,642,644,646,648,651,653,655,657,659,661,664],{"class":157,"line":349},[319,643,607],{"class":331},[319,645,610],{"class":335},[319,647,532],{"class":331},[319,649,650],{"class":342}," 'image'",[319,652,400],{"class":335},[319,654,620],{"class":331},[319,656,623],{"class":335},[319,658,626],{"class":352},[319,660,412],{"class":335},[319,662,663],{"class":342},"'\u002Fassets\u002Fcore\u002Fplaceholder.svg'",[319,665,666],{"class":335},");\n",[319,668,669,672,675,678],{"class":157,"line":359},[319,670,671],{"class":331},"  return",[319,673,674],{"class":335}," Response.",[319,676,677],{"class":352},"error",[319,679,510],{"class":335},[319,681,682],{"class":157,"line":381},[319,683,684],{"class":335},"});\n",[319,686,687],{"class":157,"line":421},[319,688,689],{"class":324},"\u002F\u002F trade-off: a generic placeholder image can confuse users if many images fail;\n",[319,691,692],{"class":157,"line":427},[319,693,694],{"class":324},"\u002F\u002F keep it neutral and small.\n",[304,696,698],{"id":697},"_4-add-retry-and-available-offline-links","4. Add retry and \"available offline\" links",[15,700,701,702,705,706,709],{},"On the offline page, listen for the ",[316,703,704],{},"online"," event to retry automatically, and list pages already in the ",[316,707,708],{},"pages"," cache so users can continue browsing.",[15,711,712],{},[35,713,42,716,42,719,42,722,42,724,42,727,42,729,42,734,42,741,42,745,42,749,42,752,42,756,42,760,42,764,42,766,42,769,42,771,42,774,42,777,42,781,42,783,42,786,42,788,42,791,42,794,42,798,42,800,42,803,42,805,42,808,42,811,42,815,42,817,42,820,42,822,42],{"viewBox":714,"width":38,"role":39,"ariaLabel":715,"style":41},"0 0 760 228","Recommended offline fallbacks for documents, images, fonts and API requests in a Workbox service worker.",[44,717],{"className":718,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],[51,720,721],{},"Fallbacks by request type",[55,723,715],{},[44,725],{"x":77,"y":77,"width":78,"height":726,"rx":80,"fill":81,"stroke":73,"style":82},"226",[84,728,721],{"x":86,"y":87,"fill":73,"style":88},[44,730],{"x":86,"y":91,"width":731,"height":732,"rx":48,"fill":73,"stroke":73,"style":733},"149.8","30.0","fill-opacity:0.06;stroke-opacity:0.4",[84,735,740],{"x":736,"y":737,"fill":73,"style":738,"textAnchor":739},"38.0","75.5","font-size:12.5px;font-weight:700","start","Request type",[44,742],{"x":743,"y":91,"width":744,"height":732,"rx":48,"fill":73,"stroke":73,"style":733},"177.8","277.1",[84,746,748],{"x":747,"y":737,"fill":73,"style":738,"textAnchor":102},"316.4","Fallback",[44,750],{"x":751,"y":91,"width":744,"height":732,"rx":48,"fill":73,"stroke":73,"style":733},"454.9",[84,753,755],{"x":754,"y":737,"fill":73,"style":738,"textAnchor":102},"593.5","Notes",[44,757],{"x":86,"y":758,"width":731,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},"86.0","stroke-opacity:0.35",[84,761,763],{"x":736,"y":762,"fill":73,"style":738,"textAnchor":739},"105.5","Document (navigation)",[44,765],{"x":743,"y":758,"width":744,"height":732,"rx":48,"fill":95,"stroke":95,"style":96},[84,767,768],{"x":747,"y":762,"fill":73,"style":107,"textAnchor":102},"offline.html",[44,770],{"x":751,"y":758,"width":744,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},[84,772,773],{"x":754,"y":762,"fill":73,"style":107,"textAnchor":102},"precached, inline CSS",[44,775],{"x":86,"y":776,"width":731,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},"116.0",[84,778,780],{"x":736,"y":779,"fill":73,"style":738,"textAnchor":739},"135.5","Image",[44,782],{"x":743,"y":776,"width":744,"height":732,"rx":48,"fill":95,"stroke":95,"style":96},[84,784,785],{"x":747,"y":779,"fill":73,"style":107,"textAnchor":102},"placeholder SVG",[44,787],{"x":751,"y":776,"width":744,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},[84,789,790],{"x":754,"y":779,"fill":73,"style":107,"textAnchor":102},"precached, tiny",[44,792],{"x":86,"y":793,"width":731,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},"146.0",[84,795,797],{"x":736,"y":796,"fill":73,"style":738,"textAnchor":739},"165.5","Font",[44,799],{"x":743,"y":793,"width":744,"height":732,"rx":48,"fill":73,"stroke":73,"style":733},[84,801,802],{"x":747,"y":796,"fill":73,"style":107,"textAnchor":102},"system font",[44,804],{"x":751,"y":793,"width":744,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},[84,806,807],{"x":754,"y":796,"fill":73,"style":107,"textAnchor":102},"let CSS fallback stack apply",[44,809],{"x":86,"y":810,"width":731,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},"176.0",[84,812,814],{"x":736,"y":813,"fill":73,"style":738,"textAnchor":739},"195.5","API data",[44,816],{"x":743,"y":810,"width":744,"height":732,"rx":48,"fill":73,"stroke":73,"style":733},[84,818,819],{"x":747,"y":813,"fill":73,"style":107,"textAnchor":102},"cached JSON or error state",[44,821],{"x":751,"y":810,"width":744,"height":732,"rx":48,"fill":81,"stroke":73,"style":759},[84,823,824],{"x":754,"y":813,"fill":73,"style":107,"textAnchor":102},"handle in the app",[178,826,828],{"id":827},"verification","Verification",[15,830,831,832,834],{},"With DevTools offline, navigate to uncached pages (fallback appears instantly), cached pages (served from the ",[316,833,708],{}," cache) and images (placeholder). With slow-network throttling, confirm the fallback or cached page appears around the timeout rather than after the browser's own timeout. Check that the precache adds only a few kilobytes to first-visit transfer.",[178,836,838],{"id":837},"worked-example-a-transit-information-site","Worked Example: A Transit Information Site",[15,840,841,842,844],{},"A public transit site's users often lost signal underground. Without a service worker they saw the browser error page and abandoned the session. The team added a precached 9KB offline page listing recently viewed timetables (read from the ",[316,843,708],{}," cache), network-first navigation with a 2.5-second timeout, and automatic retry on reconnect. Sessions that encountered a connectivity drop and continued afterwards rose from 18% to 64%, and online users saw no change in LCP because navigation preload kept network-first navigations fast.",[178,846,848],{"id":847},"keeping-offline-support-cheap-for-online-users","Keeping Offline Support Cheap for Online Users",[15,850,851,852,856,857,860,861,864],{},"Offline support must not tax the common case. Keep three costs in check: precache size (only the fallback and its minimal assets, never whole sections), worker startup (enable navigation preload so network-first navigations do not wait for boot — see ",[19,853,855],{"href":854},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fenabling-navigation-preload-in-service-workers\u002F","enabling navigation preload in service workers","), and storage growth (expiration plugins with ",[316,858,859],{},"maxEntries"," and ",[316,862,863],{},"maxAgeSeconds"," on runtime caches). Measure first-visit transfer and LCP for new visitors before and after adding the worker; both should be essentially unchanged.",[178,866,868],{"id":867},"designing-the-offline-page-itself","Designing the Offline Page Itself",[15,870,871],{},"A good offline page is honest, useful and light. State plainly that the connection is unavailable, offer an automatic retry when connectivity returns, and list content that is available offline (pages in the runtime cache, saved items). Avoid making it look like an error the user caused. Keep it within a few kilobytes: inline the critical styles, use a small inline logo, rely on system fonts, and avoid JavaScript beyond the retry and the cached-pages list. It must render perfectly with nothing but what was precached, on the slowest device you support.",[178,873,875],{"id":874},"common-mistakes","Common Mistakes",[183,877,878,884,890,896],{},[186,879,880,883],{},[189,881,882],{},"Precaching large pages \"for offline\"."," Every user downloads them on every release.",[186,885,886,889],{},[189,887,888],{},"Fallback page with external dependencies."," Fonts and stylesheets from other origins will not load offline.",[186,891,892,895],{},[189,893,894],{},"No timeout."," Flaky networks then feel worse with the worker than without.",[186,897,898,901],{},[189,899,900],{},"Forgetting to update the fallback revision."," Users keep an old offline page forever.",[178,903,905],{"id":904},"edge-cases","Edge Cases",[15,907,908,911],{},[189,909,910],{},"Authenticated pages."," Cached authenticated pages should be cleared on logout to avoid showing private data offline.",[15,913,914,917],{},[189,915,916],{},"Forms."," Form submissions offline need Background Sync or a queued retry; a fallback page alone does not save user input.",[15,919,920,923],{},[189,921,922],{},"SPAs."," Client-side navigations do not trigger navigation requests; handle offline states in the app's data layer as well.",[15,925,926,929],{},[189,927,928],{},"Analytics."," Offline sessions generate events that cannot be sent; Workbox's background sync plugin can queue analytics requests.",[178,931,933],{"id":932},"faq","FAQ",[935,936,939,943],"details",{"className":937},[938],"faq-item",[940,941,942],"summary",{},"Does an offline page affect SEO or first-visit performance?",[15,944,945],{},"No. The service worker installs after the first page load and only intercepts subsequent navigations. Crawlers do not use service workers.",[935,947,949,952],{"className":948},[938],[940,950,951],{},"Which timeout should I use?",[15,953,954],{},"Two to four seconds is typical — long enough for slow but working connections, short enough that users on dead connections see something quickly. Tune it with RUM data on navigation durations.",[935,956,958,961],{"className":957},[938],[940,959,960],{},"Can I show cached content instead of a generic offline page?",[15,962,963,964,966],{},"Yes — that is what the ",[316,965,708],{}," cache provides for previously visited pages. The generic page is the fallback for pages never visited.",[935,968,970,973],{"className":969},[938],[940,971,972],{},"Do I need Workbox for this?",[15,974,975],{},"No, but Workbox's routing, strategies and catch handler implement the pattern with little code and well-tested edge cases.",[935,977,979,982],{"className":978},[938],[940,980,981],{},"How do I keep the pages cache from growing forever?",[15,983,984,985,987,988,990],{},"Add the expiration plugin with ",[316,986,859],{}," (for example 50) and ",[316,989,863],{}," (for example 7 days) to the navigation strategy.",[935,992,994,997],{"className":993},[938],[940,995,996],{},"What about iOS?",[15,998,999],{},"Safari supports service workers and Cache Storage, with storage eviction under pressure and stricter limits for sites not added to the home screen. Keep caches small and treat offline support as best-effort.",[178,1001,1003],{"id":1002},"related","Related",[183,1005,1006,1013,1020],{},[186,1007,1008,1012],{},[19,1009,1011],{"href":1010},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fprecaching-vs-runtime-caching-with-workbox\u002F","Precaching vs runtime caching with Workbox"," — choosing what to precache.",[186,1014,1015,1019],{},[19,1016,1018],{"href":1017},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcache-invalidation-patterns\u002Finvalidating-service-worker-caches-on-deploy\u002F","Invalidating service worker caches on deploy"," — keeping fallbacks current.",[186,1021,1022,1026],{},[19,1023,1025],{"href":1024},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fdebugging-service-worker-cache-misses-in-production\u002F","Debugging service worker cache misses in production"," — when cached pages are not served.",[1028,1029,1031],"script",{"type":1030},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Add Offline Fallback Pages with Workbox\",\n  \"description\": \"Add a fast, reliable offline experience with Workbox — precached fallback pages, timeouts that do not punish slow networks, and fallbacks for images and fonts.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Precache a small, self-contained offline page\",\n      \"text\": \"Precache a small, self-contained offline page\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Route navigations network-first with a timeout\",\n      \"text\": \"navigationPreload.enable(); registerRoute(({ request }) => request.mode === 'navigate', new NetworkFirst({ cacheName: 'pages', networkTimeoutSeconds: 3 })); \u002F\u002F trade-off: a 3s timeout serves cached (possibly outdated) pages to users on \u002F\u002F very slow but working connections.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Catch failures and serve the fallback\",\n      \"text\": \"Catch failures and serve the fallback\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Add retry and \\\"available offline\\\" links\",\n      \"text\": \"On the offline page, listen for the online event to retry automatically, and list pages already in the pages cache so users can continue browsing.\"\n    }\n  ]\n}\n",[1028,1033,1034],{"type":1030},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Add Offline Fallback Pages with Workbox\",\n  \"description\": \"Add a fast, reliable offline experience with Workbox — precached fallback pages, timeouts that do not punish slow networks, and fallbacks for images and fonts.\",\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\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox\u002F\"\n  }\n}\n",[1028,1036,1037],{"type":1030},"\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\": \"Service Worker Caching Strategies\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Offline Fallback Pages with Workbox\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox\u002F\"\n    }\n  ]\n}\n",[1039,1040,1041],"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 .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 .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}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":314,"searchDepth":328,"depth":328,"links":1043},[1044,1045,1046,1052,1053,1054,1055,1056,1057,1058,1059],{"id":180,"depth":328,"text":181},{"id":213,"depth":328,"text":214},{"id":301,"depth":328,"text":302,"children":1047},[1048,1049,1050,1051],{"id":306,"depth":349,"text":307},{"id":439,"depth":349,"text":440},{"id":574,"depth":349,"text":575},{"id":697,"depth":349,"text":698},{"id":827,"depth":328,"text":828},{"id":837,"depth":328,"text":838},{"id":847,"depth":328,"text":848},{"id":867,"depth":328,"text":868},{"id":874,"depth":328,"text":875},{"id":904,"depth":328,"text":905},{"id":932,"depth":328,"text":933},{"id":1002,"depth":328,"text":1003},"Add a fast, reliable offline experience with Workbox — precached fallback pages, timeouts that do not punish slow networks, and fallbacks for images and fonts.","md",{"slug":1063,"type":1064,"breadcrumb":1065,"datePublished":1073,"dateModified":1073},"offline-fallback-pages-with-workbox","article",[1066,1069,1070,1071],{"name":1067,"url":1068},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1072},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox\u002F","2026-10-06","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox",{"title":5,"description":1076},"Serve a precached offline page when navigations fail, without slowing online users: precache a tiny fallback, use network-first with a timeout, test offline paths.","advanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Foffline-fallback-pages-with-workbox\u002Findex","E8hgMdawbyf1BAjThNDSUABTWFqcC9NKc73WXGSToPQ",[1080,1084],{"title":1081,"path":1082,"stem":1083},"Handling Service Worker Updates with skipWaiting","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fhandling-service-worker-updates-with-skipwaiting","advanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fhandling-service-worker-updates-with-skipwaiting\u002Findex",{"title":1085,"path":1086,"stem":1087},"Precaching vs Runtime Caching with Workbox","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fprecaching-vs-runtime-caching-with-workbox","advanced-caching-strategies-cdn-architecture\u002Fservice-worker-caching-strategies\u002Fprecaching-vs-runtime-caching-with-workbox\u002Findex",1791308078900]