This is an automated email from the ASF dual-hosted git repository.

moonming pushed a commit to branch fix/chunk-load-reload
in repository https://gitbox.apache.org/repos/asf/apisix-website.git

commit 8237e8b23033daf6fcc43dc8f5138af8707e8edc
Author: Ming Wen <[email protected]>
AuthorDate: Mon Jun 29 12:07:37 2026 +0800

    fix(website): auto-recover from stale code-split chunks after redeploy
    
    The site is rebuilt and republished frequently (every push to master plus a
    daily 05:00 UTC cron). Each build emits new content-hashed JS/CSS chunks and
    the publish step replaces the previous build wholesale, so a browser still 
on
    an older page can request a chunk hash that no longer exists on the CDN.
    Docusaurus client-side navigation then fails with a ChunkLoadError and 
renders
    the 404 page, even though a hard reload of the same URL serves fine. This is
    the "first visit 404, works after refresh" behavior users have reported.
    
    Add a shared client module (config/chunkReload.js) registered on all four
    Docusaurus instances (website, doc, blog/en, blog/zh). It listens for a
    ChunkLoadError via window 'error'/'unhandledrejection' and recovers with a
    single hard reload, which re-fetches the page against the current build. A
    sessionStorage guard prevents a reload loop when the chunk is genuinely
    unavailable (e.g. the browser is offline).
---
 blog/en/docusaurus.config.js |  4 +++
 blog/zh/docusaurus.config.js |  4 +++
 config/chunkReload.js        | 79 ++++++++++++++++++++++++++++++++++++++++++++
 doc/docusaurus.config.js     |  4 +++
 website/docusaurus.config.js |  4 +++
 5 files changed, 95 insertions(+)

diff --git a/blog/en/docusaurus.config.js b/blog/en/docusaurus.config.js
index 93003060185..8528c0bffc8 100644
--- a/blog/en/docusaurus.config.js
+++ b/blog/en/docusaurus.config.js
@@ -54,6 +54,10 @@ module.exports = {
       },
     },
   },
+  clientModules: [
+    // Recover from stale code-split chunks after a redeploy (see 
config/chunkReload.js).
+    require.resolve('../../config/chunkReload.js'),
+  ],
   presets: [
     [
       '@docusaurus/preset-classic',
diff --git a/blog/zh/docusaurus.config.js b/blog/zh/docusaurus.config.js
index 23d6b7b915b..e5cc976ba45 100644
--- a/blog/zh/docusaurus.config.js
+++ b/blog/zh/docusaurus.config.js
@@ -55,6 +55,10 @@ module.exports = {
       },
     },
   },
+  clientModules: [
+    // Recover from stale code-split chunks after a redeploy (see 
config/chunkReload.js).
+    require.resolve('../../config/chunkReload.js'),
+  ],
   presets: [
     [
       '@docusaurus/preset-classic',
diff --git a/config/chunkReload.js b/config/chunkReload.js
new file mode 100644
index 00000000000..1bcbced60a0
--- /dev/null
+++ b/config/chunkReload.js
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Recover from stale code-split chunks after a redeploy.
+ *
+ * The site is rebuilt and republished frequently (every push to master plus a
+ * daily cron). Each build emits content-hashed JS/CSS chunks, and the publish
+ * step replaces the previous build wholesale, so a browser that is still on an
+ * older page may request a chunk hash that no longer exists on the CDN. When
+ * that happens during client-side navigation, Docusaurus surfaces a
+ * ChunkLoadError and renders the 404 page even though a hard reload of the 
same
+ * URL serves fine (the server always returns the current build's HTML).
+ *
+ * This client module detects that specific failure and recovers with a single
+ * hard reload, which re-fetches the page against the current build. A
+ * sessionStorage guard ensures a genuinely-missing chunk (e.g. the browser is
+ * offline) cannot trigger a reload loop.
+ */
+
+const RELOAD_GUARD_KEY = 'apisix-chunk-reload-at';
+const GUARD_WINDOW_MS = 10000;
+const CHUNK_ERROR_RE = /Loading (?:CSS )?chunk [\w-]+ failed|Loading chunk \d+ 
failed|ChunkLoadError|dynamically imported module/i;
+
+function isChunkLoadError(error) {
+  if (!error) {
+    return false;
+  }
+  if (error.name === 'ChunkLoadError') {
+    return true;
+  }
+  const message = typeof error === 'string' ? error : error.message;
+  return typeof message === 'string' && CHUNK_ERROR_RE.test(message);
+}
+
+function reloadOnce() {
+  try {
+    const now = Date.now();
+    const last = Number(window.sessionStorage.getItem(RELOAD_GUARD_KEY)) || 0;
+    // Already reloaded within the guard window: the chunk is really gone
+    // (offline, or purged for good). Stop so we don't loop, and let the
+    // 404 page render instead.
+    if (now - last < GUARD_WINDOW_MS) {
+      return;
+    }
+    window.sessionStorage.setItem(RELOAD_GUARD_KEY, String(now));
+  } catch (e) {
+    // sessionStorage unavailable (private mode, blocked cookies): fall through
+    // and reload once rather than leaving the user on a broken page.
+  }
+  window.location.reload();
+}
+
+if (typeof window !== 'undefined') {
+  window.addEventListener('error', (event) => {
+    if (isChunkLoadError(event && event.error)) {
+      reloadOnce();
+    }
+  });
+  window.addEventListener('unhandledrejection', (event) => {
+    if (isChunkLoadError(event && event.reason)) {
+      reloadOnce();
+    }
+  });
+}
diff --git a/doc/docusaurus.config.js b/doc/docusaurus.config.js
index ef8c7e8d008..e3692ef9a5f 100644
--- a/doc/docusaurus.config.js
+++ b/doc/docusaurus.config.js
@@ -58,6 +58,10 @@ module.exports = {
   onBrokenLinks: 'log',
   onBrokenMarkdownLinks: 'warn',
   noIndex: false,
+  clientModules: [
+    // Recover from stale code-split chunks after a redeploy (see 
config/chunkReload.js).
+    require.resolve('../config/chunkReload.js'),
+  ],
   presets: [
     [
       '@docusaurus/preset-classic',
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index ec15903c283..2bde8acbde2 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -53,6 +53,10 @@ module.exports = {
   onBrokenLinks: 'log',
   onBrokenMarkdownLinks: 'warn',
   noIndex: false,
+  clientModules: [
+    // Recover from stale code-split chunks after a redeploy (see 
config/chunkReload.js).
+    require.resolve('../config/chunkReload.js'),
+  ],
   presets: [
     [
       '@docusaurus/preset-classic',

Reply via email to