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

Yilialinn 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 fa6d9d6b8b9 fix(seo): resolve stale coverage redirects (#2123)
fa6d9d6b8b9 is described below

commit fa6d9d6b8b971c813c8d8f36e3551d5ab90fa7a1
Author: Yilia Lin <[email protected]>
AuthorDate: Sun Sep 20 23:41:47 2026 +0800

    fix(seo): resolve stale coverage redirects (#2123)
---
 .htaccess                                     |  25 ++++-
 next/scripts/check-historical-doc-signals.mjs | 127 ++++++++++++++++++++++++++
 2 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/.htaccess b/.htaccess
index 3f5bb18b608..978caabd014 100644
--- a/.htaccess
+++ b/.htaccess
@@ -40,7 +40,7 @@ Redirect 302 "/youtube" 
"https://www.youtube.com/channel/UCgPD18cMhOg5rmPVnQhAC8
 Redirect 302 "/slack" 
"https://join.slack.com/t/the-asf/shared_invite/zt-1ugrg37is-mFrbRqBEdpGWZ7_cuvvv0A";
 Redirect 302 "/community-meeting-signup" 
"https://docs.google.com/forms/d/1C9bIJ3eh0bQrBdv4rPGxHDUvX4giNQ_IRCmDDOQ2mgE/";
 Redirect 302 "/contributor-workshop-signup" 
"https://docs.google.com/forms/d/1LUER3R9-aFsUm7MhjVd_CM1xAGnkuWIe62prFH5aqAE/";
-Redirect 302 "/guest-blog-post" "https://forms.gle/unQpSm7FyqkfaSSP8/";
+RedirectMatch 301 "^/guest-blog-post/?$" "/docs/general/blog/"
 
 # Valid entry pages for former empty directories reported as 403 in GSC.
 RedirectMatch 301 "^(/zh)?/events/?$" "$1/docs/general/events/"
@@ -157,6 +157,29 @@ RedirectMatch 301 
"^/zh/blog/2025/07/29/announcing-integration-of-apisix-and-ai-
 RedirectMatch 301 
"^/zh/blog/blog/2021/06/03/firsthand-experience-with-apache-apisix-from-ospp-2020-students/?$"
 
"/blog/blog/2021/06/03/firsthand-experience-with-apache-apisix-from-ospp-2020-students/"
 RedirectMatch 301 "^/docs/apisix/getting-started/$" 
"/docs/apisix/getting-started/README/"
 RedirectMatch 301 "^/zh/docs/apisix/getting-started/$" 
"/zh/docs/apisix/getting-started/README/"
+
+# Resolve retired documentation and stale blog paths reported as soft 404s.
+RedirectMatch 301 "^/docs/java-plugin-runner/0\.1/how-it-works/?$" 
"/docs/java-plugin-runner/how-it-works/"
+RedirectMatch 301 
"^/docs/java-plugin-runner/0\.1/the-internal-of-apisix-java-plugin-runner/?$" 
"/docs/java-plugin-runner/the-internal-of-apisix-java-plugin-runner/"
+RedirectMatch 301 "^/zh/docs/java-plugin-runner/0\.1/development/?$" 
"/zh/docs/java-plugin-runner/development/"
+RedirectMatch 301 "^/zh/docs/java-plugin-runner/0\.1/how-it-works/?$" 
"/zh/docs/java-plugin-runner/how-it-works/"
+RedirectMatch 301 
"^/zh/docs/java-plugin-runner/0\.1/the-internal-of-apisix-java-plugin-runner/?$"
 "/zh/docs/java-plugin-runner/the-internal-of-apisix-java-plugin-runner/"
+RedirectMatch 301 "^/blog/2023/12/01/datavisor-uses-apisix/?$" 
"/blog/2023/12/19/datavisor-uses-apisix/"
+RedirectMatch 301 
"^/zh/blog/2021/08/16/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/?$"
 
"/zh/blog/2021/08/25/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/"
+RedirectMatch 301 "^/zh/blog/2021/10/29/extension-guide/?$" 
"/zh/blog/2021/10/26/extension-guide/"
+RedirectMatch 301 
"^/blog/2023/03/08/why-do-microservices-need-an-api-gateway/?$" 
"/blog/2023/05/19/why-do-microservices-need-an-api-gateway/"
+RedirectMatch 301 
"^/zh/blog/2020/12/16/another-way-to-implement-envoy-filter/?$" 
"/blog/2020/12/16/another-way-to-implement-envoy-filter/"
+RedirectMatch 301 "^/blog/2023/12/08/zhengcaiyun-uses-apisix/?$" 
"/blog/2023/12/26/zhengcaiyun-uses-apisix/"
+RedirectMatch 301 "^/blog/2021/09/29/release-apache-apisix-2\.10-en/?$" 
"/blog/2021/09/29/release-apache-apisix-2.10/"
+RedirectMatch 301 "^/blog/2022/10/28/apisix-ingress-with-horizon-ai/?$" 
"/zh/blog/2022/10/28/apisix-ingress-with-horizon-ai/"
+RedirectMatch 301 "^/blog/2023/07/12/2023-apisix-meetup-malaysia/?$" 
"/blog/2023/07/11/2023-apisix-meetup-malaysia/"
+RedirectMatch 301 
"^/docs/java-plugin-runner/0\.(?:3|4)\.0/installation-guide/development\.md/?$" 
"/docs/java-plugin-runner/development/"
+RedirectMatch 301 
"^/zh/docs/java-plugin-runner/0\.4\.0/installation-guide/development\.md/?$" 
"/zh/docs/java-plugin-runner/development/"
+RedirectMatch 301 "^/zh/blog/2023/03/10/release-apache-apisix-3\.2\.0/?$" 
"/zh/blog/2023/03/09/release-apache-apisix-3.2.0/"
+RedirectMatch 301 
"^/zh/blog/2023/05/19/why-do-microservices-need-an-api-gateway/?$" 
"/zh/blog/2023/03/08/why-do-microservices-need-an-api-gateway/"
+RedirectMatch 301 "^/zh/blog/2023/12/19/datavisor-uses-apisix/?$" 
"/zh/blog/2023/12/01/datavisor-uses-apisix/"
+RedirectMatch 301 "^/zh/blog/2023/12/26/zhengcaiyun-uses-apisix/?$" 
"/zh/blog/2023/12/08/zhengcaiyun-uses-apisix/"
+RedirectMatch 301 "^/zh/blog/2024/04/05/build-apisix-in-sles15/?$" 
"/blog/2024/04/05/build-apisix-in-sles15/"
 RedirectMatch 301 "^/docs/apisix/3\.2/plugins/(.*)$" 
"https://apache-apisix.netlify.app/docs/apisix/3.2/plugins/$1";
 RedirectMatch 301 "^/zh/docs/apisix/3\.2/plugins/(.*)$" 
"https://apache-apisix.netlify.app/zh/docs/apisix/3.2/plugins/$1";
 
diff --git a/next/scripts/check-historical-doc-signals.mjs 
b/next/scripts/check-historical-doc-signals.mjs
index b3a78d9d191..7f0d445cf83 100644
--- a/next/scripts/check-historical-doc-signals.mjs
+++ b/next/scripts/check-historical-doc-signals.mjs
@@ -1,4 +1,5 @@
 import assert from 'node:assert/strict';
+import fs from 'node:fs';
 import path from 'node:path';
 import { fileURLToPath } from 'node:url';
 
@@ -9,6 +10,132 @@ const signalsPath = path.resolve(
 );
 const { getVersionedDocSignals } = await import(signalsPath);
 const site = 'https://apisix.apache.org';
+const htaccess = fs.readFileSync(path.resolve(scriptDirectory, 
'../../.htaccess'), 'utf8');
+const redirectDirectives = htaccess
+  .split('\n')
+  .map((line) => line.trim())
+  .filter((line) => /^Redirect(?:Match)?\s+3\d{2}\s+"/.test(line))
+  .map((line) => {
+    const redirectMatch = line.match(
+      /^RedirectMatch\s+(\d{3})\s+"([^"]+)"\s+"([^"]+)"$/,
+    );
+    if (redirectMatch) {
+      return {
+        type: 'match',
+        status: Number(redirectMatch[1]),
+        source: new RegExp(redirectMatch[2]),
+        destination: redirectMatch[3],
+      };
+    }
+
+    const redirect = 
line.match(/^Redirect\s+(\d{3})\s+"([^"]+)"\s+"([^"]+)"$/);
+    assert(redirect, `Unable to parse redirect directive: ${line}`);
+    return {
+      type: 'prefix',
+      status: Number(redirect[1]),
+      source: redirect[2],
+      destination: redirect[3],
+    };
+  });
+
+function firstRedirect(requestPath) {
+  for (const directive of redirectDirectives) {
+    if (directive.type === 'match' && directive.source.test(requestPath)) {
+      return {
+        status: directive.status,
+        destination: requestPath.replace(directive.source, 
directive.destination),
+      };
+    }
+    const matchesPrefix =
+      directive.type === 'prefix'
+      && (requestPath === directive.source
+        || requestPath.startsWith(
+          directive.source.endsWith('/') ? directive.source : 
`${directive.source}/`,
+        ));
+    if (matchesPrefix) {
+      return {
+        status: directive.status,
+        destination: 
`${directive.destination}${requestPath.slice(directive.source.length)}`,
+      };
+    }
+  }
+  return null;
+}
+
+const coverageRedirects = [
+  ['^/docs/java-plugin-runner/0\\.1/how-it-works/?$', 
'/docs/java-plugin-runner/how-it-works/'],
+  
['^/docs/java-plugin-runner/0\\.1/the-internal-of-apisix-java-plugin-runner/?$',
 '/docs/java-plugin-runner/the-internal-of-apisix-java-plugin-runner/'],
+  ['^/zh/docs/java-plugin-runner/0\\.1/development/?$', 
'/zh/docs/java-plugin-runner/development/'],
+  ['^/zh/docs/java-plugin-runner/0\\.1/how-it-works/?$', 
'/zh/docs/java-plugin-runner/how-it-works/'],
+  
['^/zh/docs/java-plugin-runner/0\\.1/the-internal-of-apisix-java-plugin-runner/?$',
 '/zh/docs/java-plugin-runner/the-internal-of-apisix-java-plugin-runner/'],
+  ['^/blog/2023/12/01/datavisor-uses-apisix/?$', 
'/blog/2023/12/19/datavisor-uses-apisix/'],
+  
['^/zh/blog/2021/08/16/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/?$',
 
'/zh/blog/2021/08/25/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/'],
+  ['^/zh/blog/2021/10/29/extension-guide/?$', 
'/zh/blog/2021/10/26/extension-guide/'],
+  ['^/blog/2023/03/08/why-do-microservices-need-an-api-gateway/?$', 
'/blog/2023/05/19/why-do-microservices-need-an-api-gateway/'],
+  ['^/zh/blog/2020/12/16/another-way-to-implement-envoy-filter/?$', 
'/blog/2020/12/16/another-way-to-implement-envoy-filter/'],
+  ['^/blog/2023/12/08/zhengcaiyun-uses-apisix/?$', 
'/blog/2023/12/26/zhengcaiyun-uses-apisix/'],
+  ['^/blog/2021/09/29/release-apache-apisix-2\\.10-en/?$', 
'/blog/2021/09/29/release-apache-apisix-2.10/'],
+  ['^/blog/2022/10/28/apisix-ingress-with-horizon-ai/?$', 
'/zh/blog/2022/10/28/apisix-ingress-with-horizon-ai/'],
+  ['^/blog/2023/07/12/2023-apisix-meetup-malaysia/?$', 
'/blog/2023/07/11/2023-apisix-meetup-malaysia/'],
+  
['^/docs/java-plugin-runner/0\\.(?:3|4)\\.0/installation-guide/development\\.md/?$',
 '/docs/java-plugin-runner/development/'],
+  
['^/zh/docs/java-plugin-runner/0\\.4\\.0/installation-guide/development\\.md/?$',
 '/zh/docs/java-plugin-runner/development/'],
+  ['^/zh/blog/2023/03/10/release-apache-apisix-3\\.2\\.0/?$', 
'/zh/blog/2023/03/09/release-apache-apisix-3.2.0/'],
+  ['^/zh/blog/2023/05/19/why-do-microservices-need-an-api-gateway/?$', 
'/zh/blog/2023/03/08/why-do-microservices-need-an-api-gateway/'],
+  ['^/zh/blog/2023/12/19/datavisor-uses-apisix/?$', 
'/zh/blog/2023/12/01/datavisor-uses-apisix/'],
+  ['^/zh/blog/2023/12/26/zhengcaiyun-uses-apisix/?$', 
'/zh/blog/2023/12/08/zhengcaiyun-uses-apisix/'],
+  ['^/zh/blog/2024/04/05/build-apisix-in-sles15/?$', 
'/blog/2024/04/05/build-apisix-in-sles15/'],
+  ['^/guest-blog-post/?$', '/docs/general/blog/'],
+];
+
+const coverageRedirectCases = [
+  ['/blog/2021/09/29/release-apache-apisix-2.10-en', 
'/blog/2021/09/29/release-apache-apisix-2.10/'],
+  ['/blog/2022/10/28/apisix-ingress-with-horizon-ai/', 
'/zh/blog/2022/10/28/apisix-ingress-with-horizon-ai/'],
+  ['/blog/2023/03/08/why-do-microservices-need-an-api-gateway/', 
'/blog/2023/05/19/why-do-microservices-need-an-api-gateway/'],
+  ['/blog/2023/07/12/2023-apisix-meetup-malaysia/', 
'/blog/2023/07/11/2023-apisix-meetup-malaysia/'],
+  ['/blog/2023/12/01/datavisor-uses-apisix/', 
'/blog/2023/12/19/datavisor-uses-apisix/'],
+  ['/blog/2023/12/08/zhengcaiyun-uses-apisix/', 
'/blog/2023/12/26/zhengcaiyun-uses-apisix/'],
+  ['/docs/java-plugin-runner/0.1/how-it-works/', 
'/docs/java-plugin-runner/how-it-works/'],
+  ['/docs/java-plugin-runner/0.1/the-internal-of-apisix-java-plugin-runner/', 
'/docs/java-plugin-runner/the-internal-of-apisix-java-plugin-runner/'],
+  ['/docs/java-plugin-runner/0.3.0/installation-guide/development.md/', 
'/docs/java-plugin-runner/development/'],
+  ['/docs/java-plugin-runner/0.4.0/installation-guide/development.md/', 
'/docs/java-plugin-runner/development/'],
+  ['/guest-blog-post', '/docs/general/blog/'],
+  ['/zh/blog/2020/12/16/another-way-to-implement-envoy-filter/', 
'/blog/2020/12/16/another-way-to-implement-envoy-filter/'],
+  
['/zh/blog/2021/08/16/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication',
 
'/zh/blog/2021/08/25/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/'],
+  
['/zh/blog/2021/08/16/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/',
 
'/zh/blog/2021/08/25/using-the-apache-apisix-openid-connect-plugin-for-centralized-authentication/'],
+  ['/zh/blog/2021/10/29/extension-guide/', 
'/zh/blog/2021/10/26/extension-guide/'],
+  ['/zh/blog/2023/03/10/release-apache-apisix-3.2.0/', 
'/zh/blog/2023/03/09/release-apache-apisix-3.2.0/'],
+  ['/zh/blog/2023/05/19/why-do-microservices-need-an-api-gateway/', 
'/zh/blog/2023/03/08/why-do-microservices-need-an-api-gateway/'],
+  ['/zh/blog/2023/12/19/datavisor-uses-apisix/', 
'/zh/blog/2023/12/01/datavisor-uses-apisix/'],
+  ['/zh/blog/2023/12/26/zhengcaiyun-uses-apisix/', 
'/zh/blog/2023/12/08/zhengcaiyun-uses-apisix/'],
+  ['/zh/blog/2024/04/05/build-apisix-in-sles15/', 
'/blog/2024/04/05/build-apisix-in-sles15/'],
+  ['/zh/docs/java-plugin-runner/0.1/development/', 
'/zh/docs/java-plugin-runner/development/'],
+  ['/zh/docs/java-plugin-runner/0.1/how-it-works/', 
'/zh/docs/java-plugin-runner/how-it-works/'],
+  
['/zh/docs/java-plugin-runner/0.1/the-internal-of-apisix-java-plugin-runner/', 
'/zh/docs/java-plugin-runner/the-internal-of-apisix-java-plugin-runner/'],
+  ['/zh/docs/java-plugin-runner/0.4.0/installation-guide/development.md/', 
'/zh/docs/java-plugin-runner/development/'],
+];
+
+for (const [source, target] of coverageRedirects) {
+  assert.ok(
+    htaccess.includes(`RedirectMatch 301 "${source}" "${target}"`),
+    `${source} should redirect directly to ${target}`,
+  );
+}
+
+for (const [sourcePath, expectedDestination] of coverageRedirectCases) {
+  const redirect = firstRedirect(sourcePath);
+  assert(redirect, `${sourcePath} should match a redirect`);
+  assert.equal(redirect.status, 301, `${sourcePath} should return a permanent 
redirect`);
+  assert.equal(
+    redirect.destination,
+    expectedDestination,
+    `${sourcePath} should redirect directly to its verified replacement`,
+  );
+  assert.equal(
+    firstRedirect(expectedDestination),
+    null,
+    `${expectedDestination} should not start another redirect`,
+  );
+}
 
 assert.equal(
   getVersionedDocSignals('/docs/apisix/installation-guide/', site, null),

Reply via email to