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

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


The following commit(s) were added to refs/heads/master by this push:
     new a23a848b43a fix(website): auto-recover the 404 page from stale builds 
after redeploy (#2064)
a23a848b43a is described below

commit a23a848b43acb8224167f4028e243d2683db424f
Author: Ming Wen <[email protected]>
AuthorDate: Mon Jun 29 13:27:51 2026 +0800

    fix(website): auto-recover the 404 page from stale builds after redeploy 
(#2064)
    
    The site is rebuilt and republished frequently (every push to master plus a
    daily 05:00 UTC cron), and each build replaces the previous build's
    content-hashed chunks. A browser still showing a page from an older build 
holds
    that build's route manifest; clicking through to a route that only exists 
in a
    newer build finds no client-side match and renders the 404 page, even 
though a
    hard load of the same URL is served fine (HTTP 200). This is the reported
    "first visit 404, works after refresh" behavior.
    
    The repo already swizzles a custom theme/NotFound in all three Docusaurus
    instances (website, doc, blog). Add a guarded recovery there: when the 404 
is
    reached via a client-side navigation (the document the server returned was 
for a
    different path), reload once so the current build is fetched. Genuine 404s 
--
    the server returned this page directly -- are left untouched.
    
    Detection uses PerformanceNavigationTiming to compare the document's load 
path
    with the current path, so real broken links do not trigger a wasted reload, 
and
    a per-path sessionStorage guard reloads at most once as a backstop.
---
 blog/src/theme/NotFound/index.tsx    | 138 +++++++++++++++++++++--------------
 doc/src/theme/NotFound/index.tsx     | 138 +++++++++++++++++++++--------------
 website/src/theme/NotFound/index.tsx | 138 +++++++++++++++++++++--------------
 3 files changed, 255 insertions(+), 159 deletions(-)

diff --git a/blog/src/theme/NotFound/index.tsx 
b/blog/src/theme/NotFound/index.tsx
index c8a513b32b1..6a43297540d 100644
--- a/blog/src/theme/NotFound/index.tsx
+++ b/blog/src/theme/NotFound/index.tsx
@@ -1,6 +1,6 @@
 /* eslint-disable jsx-a11y/anchor-is-valid */
 import type { FC } from 'react';
-import React from 'react';
+import React, { useEffect } from 'react';
 import Layout from '@theme/Layout';
 import Head from '@docusaurus/Head';
 import { translate } from '@docusaurus/Translate';
@@ -8,57 +8,89 @@ import Link from '@docusaurus/Link';
 import style from './styles.module.scss';
 import Fitty from './Fitty';
 
-const NotFound: FC = () => (
-  <Layout
-    title={translate({
-      id: 'theme.NotFound.title',
-      message: 'Page Not Found',
-    })}
-  >
-    <Head>
-      <meta name="robots" content="noindex,follow" />
-    </Head>
-    <main className={style.container}>
-      <section>
-        <Fitty tagName="h1" contentEditable>404</Fitty>
-        <Fitty tagName="h2">
-          Page Not Found
-        </Fitty>
-      </section>
-      <p>
-        We could not find what you were looking for.
-      </p>
-      <p>
-        If you think this link should not be broken, please
-        {' '}
-        <Link 
href="https://github.com/apache/apisix-website/issues/new/choose"; 
target="_blank" rel="noreferrer">submit an Issue</Link>
-        .
-      </p>
-      <p>
-        You can also open the
-        {' '}
-        <Link to="/">home page</Link>
-        ,
-        {' '}
-        <Link to="/docs/">documentation</Link>
-        ,
-        {' '}
-        <Link to="/blog/">blog</Link>
-        , or
-        {' '}
-        <a
-          role="button"
-          href="#"
-          onClick={() => {
-            window?.history.back();
-          }}
-        >
-          return to the source page
-        </a>
-        .
-      </p>
-    </main>
-  </Layout>
-);
+const NotFound: FC = () => {
+  useEffect(() => {
+    // The site is rebuilt and republished frequently, and each build replaces 
the
+    // previous one's content-hashed chunks. A page still open from an older 
build
+    // can client-navigate to a route that is missing from its stale route 
manifest
+    // and land here, even though the server serves that page fine. Recover 
with a
+    // single hard reload so the current build is fetched.
+    //
+    // Only do this when the 404 surfaced during a client-side navigation, not 
on
+    // the document the server returned directly — that is a genuine 404 and is
+    // left untouched. A per-path guard reloads at most once as a safety 
backstop.
+    try {
+      if (typeof window === 'undefined' || !window.performance) {
+        return;
+      }
+      const [nav] = window.performance.getEntriesByType('navigation');
+      const { pathname } = window.location;
+      if (!nav || new URL(nav.name).pathname === pathname) {
+        return;
+      }
+      const guardKey = `apisix-404-reload:${pathname}`;
+      if (window.sessionStorage.getItem(guardKey)) {
+        return;
+      }
+      window.sessionStorage.setItem(guardKey, '1');
+      window.location.reload();
+    } catch {
+      // performance / sessionStorage / URL unavailable — leave the 404 as-is.
+    }
+  }, []);
+
+  return (
+    <Layout
+      title={translate({
+        id: 'theme.NotFound.title',
+        message: 'Page Not Found',
+      })}
+    >
+      <Head>
+        <meta name="robots" content="noindex,follow" />
+      </Head>
+      <main className={style.container}>
+        <section>
+          <Fitty tagName="h1" contentEditable>404</Fitty>
+          <Fitty tagName="h2">
+            Page Not Found
+          </Fitty>
+        </section>
+        <p>
+          We could not find what you were looking for.
+        </p>
+        <p>
+          If you think this link should not be broken, please
+          {' '}
+          <Link 
href="https://github.com/apache/apisix-website/issues/new/choose"; 
target="_blank" rel="noreferrer">submit an Issue</Link>
+          .
+        </p>
+        <p>
+          You can also open the
+          {' '}
+          <Link to="/">home page</Link>
+          ,
+          {' '}
+          <Link to="/docs/">documentation</Link>
+          ,
+          {' '}
+          <Link to="/blog/">blog</Link>
+          , or
+          {' '}
+          <a
+            role="button"
+            href="#"
+            onClick={() => {
+              window?.history.back();
+            }}
+          >
+            return to the source page
+          </a>
+          .
+        </p>
+      </main>
+    </Layout>
+  );
+};
 
 export default NotFound;
diff --git a/doc/src/theme/NotFound/index.tsx b/doc/src/theme/NotFound/index.tsx
index c8a513b32b1..6a43297540d 100644
--- a/doc/src/theme/NotFound/index.tsx
+++ b/doc/src/theme/NotFound/index.tsx
@@ -1,6 +1,6 @@
 /* eslint-disable jsx-a11y/anchor-is-valid */
 import type { FC } from 'react';
-import React from 'react';
+import React, { useEffect } from 'react';
 import Layout from '@theme/Layout';
 import Head from '@docusaurus/Head';
 import { translate } from '@docusaurus/Translate';
@@ -8,57 +8,89 @@ import Link from '@docusaurus/Link';
 import style from './styles.module.scss';
 import Fitty from './Fitty';
 
-const NotFound: FC = () => (
-  <Layout
-    title={translate({
-      id: 'theme.NotFound.title',
-      message: 'Page Not Found',
-    })}
-  >
-    <Head>
-      <meta name="robots" content="noindex,follow" />
-    </Head>
-    <main className={style.container}>
-      <section>
-        <Fitty tagName="h1" contentEditable>404</Fitty>
-        <Fitty tagName="h2">
-          Page Not Found
-        </Fitty>
-      </section>
-      <p>
-        We could not find what you were looking for.
-      </p>
-      <p>
-        If you think this link should not be broken, please
-        {' '}
-        <Link 
href="https://github.com/apache/apisix-website/issues/new/choose"; 
target="_blank" rel="noreferrer">submit an Issue</Link>
-        .
-      </p>
-      <p>
-        You can also open the
-        {' '}
-        <Link to="/">home page</Link>
-        ,
-        {' '}
-        <Link to="/docs/">documentation</Link>
-        ,
-        {' '}
-        <Link to="/blog/">blog</Link>
-        , or
-        {' '}
-        <a
-          role="button"
-          href="#"
-          onClick={() => {
-            window?.history.back();
-          }}
-        >
-          return to the source page
-        </a>
-        .
-      </p>
-    </main>
-  </Layout>
-);
+const NotFound: FC = () => {
+  useEffect(() => {
+    // The site is rebuilt and republished frequently, and each build replaces 
the
+    // previous one's content-hashed chunks. A page still open from an older 
build
+    // can client-navigate to a route that is missing from its stale route 
manifest
+    // and land here, even though the server serves that page fine. Recover 
with a
+    // single hard reload so the current build is fetched.
+    //
+    // Only do this when the 404 surfaced during a client-side navigation, not 
on
+    // the document the server returned directly — that is a genuine 404 and is
+    // left untouched. A per-path guard reloads at most once as a safety 
backstop.
+    try {
+      if (typeof window === 'undefined' || !window.performance) {
+        return;
+      }
+      const [nav] = window.performance.getEntriesByType('navigation');
+      const { pathname } = window.location;
+      if (!nav || new URL(nav.name).pathname === pathname) {
+        return;
+      }
+      const guardKey = `apisix-404-reload:${pathname}`;
+      if (window.sessionStorage.getItem(guardKey)) {
+        return;
+      }
+      window.sessionStorage.setItem(guardKey, '1');
+      window.location.reload();
+    } catch {
+      // performance / sessionStorage / URL unavailable — leave the 404 as-is.
+    }
+  }, []);
+
+  return (
+    <Layout
+      title={translate({
+        id: 'theme.NotFound.title',
+        message: 'Page Not Found',
+      })}
+    >
+      <Head>
+        <meta name="robots" content="noindex,follow" />
+      </Head>
+      <main className={style.container}>
+        <section>
+          <Fitty tagName="h1" contentEditable>404</Fitty>
+          <Fitty tagName="h2">
+            Page Not Found
+          </Fitty>
+        </section>
+        <p>
+          We could not find what you were looking for.
+        </p>
+        <p>
+          If you think this link should not be broken, please
+          {' '}
+          <Link 
href="https://github.com/apache/apisix-website/issues/new/choose"; 
target="_blank" rel="noreferrer">submit an Issue</Link>
+          .
+        </p>
+        <p>
+          You can also open the
+          {' '}
+          <Link to="/">home page</Link>
+          ,
+          {' '}
+          <Link to="/docs/">documentation</Link>
+          ,
+          {' '}
+          <Link to="/blog/">blog</Link>
+          , or
+          {' '}
+          <a
+            role="button"
+            href="#"
+            onClick={() => {
+              window?.history.back();
+            }}
+          >
+            return to the source page
+          </a>
+          .
+        </p>
+      </main>
+    </Layout>
+  );
+};
 
 export default NotFound;
diff --git a/website/src/theme/NotFound/index.tsx 
b/website/src/theme/NotFound/index.tsx
index c8a513b32b1..6a43297540d 100644
--- a/website/src/theme/NotFound/index.tsx
+++ b/website/src/theme/NotFound/index.tsx
@@ -1,6 +1,6 @@
 /* eslint-disable jsx-a11y/anchor-is-valid */
 import type { FC } from 'react';
-import React from 'react';
+import React, { useEffect } from 'react';
 import Layout from '@theme/Layout';
 import Head from '@docusaurus/Head';
 import { translate } from '@docusaurus/Translate';
@@ -8,57 +8,89 @@ import Link from '@docusaurus/Link';
 import style from './styles.module.scss';
 import Fitty from './Fitty';
 
-const NotFound: FC = () => (
-  <Layout
-    title={translate({
-      id: 'theme.NotFound.title',
-      message: 'Page Not Found',
-    })}
-  >
-    <Head>
-      <meta name="robots" content="noindex,follow" />
-    </Head>
-    <main className={style.container}>
-      <section>
-        <Fitty tagName="h1" contentEditable>404</Fitty>
-        <Fitty tagName="h2">
-          Page Not Found
-        </Fitty>
-      </section>
-      <p>
-        We could not find what you were looking for.
-      </p>
-      <p>
-        If you think this link should not be broken, please
-        {' '}
-        <Link 
href="https://github.com/apache/apisix-website/issues/new/choose"; 
target="_blank" rel="noreferrer">submit an Issue</Link>
-        .
-      </p>
-      <p>
-        You can also open the
-        {' '}
-        <Link to="/">home page</Link>
-        ,
-        {' '}
-        <Link to="/docs/">documentation</Link>
-        ,
-        {' '}
-        <Link to="/blog/">blog</Link>
-        , or
-        {' '}
-        <a
-          role="button"
-          href="#"
-          onClick={() => {
-            window?.history.back();
-          }}
-        >
-          return to the source page
-        </a>
-        .
-      </p>
-    </main>
-  </Layout>
-);
+const NotFound: FC = () => {
+  useEffect(() => {
+    // The site is rebuilt and republished frequently, and each build replaces 
the
+    // previous one's content-hashed chunks. A page still open from an older 
build
+    // can client-navigate to a route that is missing from its stale route 
manifest
+    // and land here, even though the server serves that page fine. Recover 
with a
+    // single hard reload so the current build is fetched.
+    //
+    // Only do this when the 404 surfaced during a client-side navigation, not 
on
+    // the document the server returned directly — that is a genuine 404 and is
+    // left untouched. A per-path guard reloads at most once as a safety 
backstop.
+    try {
+      if (typeof window === 'undefined' || !window.performance) {
+        return;
+      }
+      const [nav] = window.performance.getEntriesByType('navigation');
+      const { pathname } = window.location;
+      if (!nav || new URL(nav.name).pathname === pathname) {
+        return;
+      }
+      const guardKey = `apisix-404-reload:${pathname}`;
+      if (window.sessionStorage.getItem(guardKey)) {
+        return;
+      }
+      window.sessionStorage.setItem(guardKey, '1');
+      window.location.reload();
+    } catch {
+      // performance / sessionStorage / URL unavailable — leave the 404 as-is.
+    }
+  }, []);
+
+  return (
+    <Layout
+      title={translate({
+        id: 'theme.NotFound.title',
+        message: 'Page Not Found',
+      })}
+    >
+      <Head>
+        <meta name="robots" content="noindex,follow" />
+      </Head>
+      <main className={style.container}>
+        <section>
+          <Fitty tagName="h1" contentEditable>404</Fitty>
+          <Fitty tagName="h2">
+            Page Not Found
+          </Fitty>
+        </section>
+        <p>
+          We could not find what you were looking for.
+        </p>
+        <p>
+          If you think this link should not be broken, please
+          {' '}
+          <Link 
href="https://github.com/apache/apisix-website/issues/new/choose"; 
target="_blank" rel="noreferrer">submit an Issue</Link>
+          .
+        </p>
+        <p>
+          You can also open the
+          {' '}
+          <Link to="/">home page</Link>
+          ,
+          {' '}
+          <Link to="/docs/">documentation</Link>
+          ,
+          {' '}
+          <Link to="/blog/">blog</Link>
+          , or
+          {' '}
+          <a
+            role="button"
+            href="#"
+            onClick={() => {
+              window?.history.back();
+            }}
+          >
+            return to the source page
+          </a>
+          .
+        </p>
+      </main>
+    </Layout>
+  );
+};
 
 export default NotFound;

Reply via email to