Tag 归档页 0 篇文章:清理后空架——审计、合并、最低门槛 + 410 Gone

tag 页存在但 0 篇已发布文章。审计 tag 使用、要求每 tag 最少 N 篇、自动去索引或 410 空归档。

审计 tag 页——23 个显示”No articles found”。它们用的 tag 来自你后来删掉的文章、或从未发布的草稿。空页还在渲染、还在 sitemap 里、Google 还在爬——Search Console 标为”Crawled — currently not indexed”或更糟”Soft 404”。对用户毫无帮助、还在拉低站点质量信号。

空 tag 页是任何内容清理的天然副产物。修法两层:回填(审计存量空 tag、去索引或 410)+ 预防(prebuild 规则:tag 必须至少有 N 篇已发布非草稿才渲染)。

常见原因

1. 用了某 tag 的文章被删

批量删了薄文——其中有些是某 tag 的唯一用户——那个 tag 归档页现在 0 内容。

如何判断:列 frontmatter 用到的所有 tag、查文章计数。

# scripts/count-tags.mjs
import fs from "node:fs";
import matter from "gray-matter";
const counts = new Map();
for (const f of /* 遍历文章 */ []) {
  const { data } = matter(fs.readFileSync(f, "utf8"));
  if (data.draft) continue;
  for (const t of (data.tags || [])) {
    counts.set(t, (counts.get(t) || 0) + 1);
  }
}
// 然后和 tag config / generator 中声明的 tag

tag config 里 count 0 的——orphan。

2. 用某 tag 的文章全被设草稿

一次质量行动把某些文章批量设草稿——剩下的 tag 0 篇已发布——tag 页渲染”无文章”空态。

如何判断:同脚本——计数时排除 draft: true

3. tag 分类有拼写制造重复

你有 ai-toolsai-tool 两个 tag——复数有文章、单数没(或反过来)——一个归档页空、另一个有内容。

如何判断:列 tag、字母序排、扫近重。

grep -rh '^tags:' src/content/articles/ | tr ',' '\n' | sort -u

4. tag 生成器为每个 frontmatter 值都自动建页

tag 生成器走 frontmatter、为每个不同 tag 值建一页、无最小门槛。一次性 tag(单篇文章里的 tag: "expirimental-feature")有一页 tag 页大概会一直空。

如何判断:按 tag 计数文章——1 篇是”瘦 tag”、0 篇是 orphan。

5. 改 tag 名没 redirect

你把 chatgpt 改成 chat-gpt(或反向)——老 tag 页还在但无文章引用——sitemap 还列——404 或 soft-404。

如何判断:URL 路径中存在但当前任何文章 frontmatter 都没用到的 tag。

最短修复路径

Step 1:盘点 orphan tag

跑完整 tag 使用报告:

# scripts/audit-tag-usage.mjs
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";

const counts = new Map();

function walk(dir) {
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
    const p = path.join(dir, e.name);
    if (e.isDirectory()) { walk(p); continue; }
    if (!p.endsWith(".mdx")) continue;
    const { data } = matter(fs.readFileSync(p, "utf8"));
    if (data.draft) continue;
    for (const t of (data.tags || [])) {
      counts.set(t, (counts.get(t) || 0) + 1);
    }
  }
}
walk("src/content/articles");

for (const [tag, n] of [...counts.entries()].sort((a, b) => a[1] - b[1])) {
  if (n <= 1) console.log(`THIN tag "${tag}": ${n} articles`);
}

0 篇——orphan;1 篇——瘦 tag、大概也不该有 tag 页。

Step 2:按 tag 决定——去索引、合并、回填

每个 orphan/瘦 tag:

- 合并到相似 tag(在文章 frontmatter 中改名、tag 页 redirect)
- 去索引(noindex meta + 从 sitemap 移除)
- 服务器对那个 tag URL 返回 410 Gone
- 有价值——手写 2+ 篇填进去

合并通常最好——折叠近重、集中权威。

Step 3:prebuild 规则——tag 必须 N+ 篇

设最小(如 3 篇已发布)才渲染 archive:

// src/pages/[lang]/tags/[tag].astro
export async function getStaticPaths() {
  const all = await getCollection("articles");
  const MIN = 3;
  const counts = new Map();
  for (const a of all) {
    if (a.data.draft) continue;
    for (const t of (a.data.tags || [])) {
      counts.set(`${a.data.lang}:${t}`, (counts.get(`${a.data.lang}:${t}`) || 0) + 1);
    }
  }
  return [...counts.entries()]
    .filter(([_, n]) => n >= MIN)
    .map(([key]) => {
      const [lang, tag] = key.split(":");
      return { params: { lang, tag } };
    });
}

现在只有 3+ 篇的 tag 有页——空 tag 页不复存在。

Step 4:sitemap 不带去索引/移除的 tag

生 sitemap 的代码也要遵守 MIN 门槛:

// src/pages/sitemap.xml.ts (草图)
const tagsToInclude = /* 同上过滤 */;
// 只为这些发 <url>

之前被索引过的 tag URL——也让服务器返 410 让 Google 尽快移除。Astro 静态产出——在托管层加 redirect/410(Netlify _redirects、Cloudflare Pages _redirects 等):

/en/tags/deprecated-tag/  410!
/zh/tags/deprecated-tag/  410!

Step 5:请求重抓 tag 索引页(如有)

如果有 /tags/ 总览页列所有 tag——请求重抓让 Google 看到更新列表。被移除的单个 tag URL——410 在下次抓时触发移除。

预防

  • tag 生成器强制每 tag 最少 N 篇(3 合理)
  • sitemap 排除门槛以下 tag
  • 移除 tag 在托管层返 410
  • 改 tag 名必须同 PR 改所有用它的文章
  • 季度 tag 审计——合并近重 tag、修剪瘦 tag
  • frontmatter tag 值对受控词汇表校验(可选但最干净)

相关

标签: #内容运营 #站点质量 #站点审计 #排查 #标签页