导致 Google 忽略我整个站点的 Next.js SEO Bug(以及我如何发现它)

发布: (2026年3月7日 GMT+8 11:51)
13 分钟阅读
原文: Dev.to

Source: Dev.to

一些背景

我不是开发者。我喜欢动手做东西,尝试新工具。SEO 总是那件我会“以后再弄清楚”的事。著名的最后一句话。

我对 SEO 有很高层次的了解,但在营销绩效工作中,我明白一个被良好索引的网站和正确关键词的重要性。我原以为这基本上就是全部——研究查询并围绕它们创建优质内容。

这一次,我必须 弄清楚!

正如我所说,我通过构建东西来学习新工具和技术。

这个应用叫 MonkeyTravel。它使用 AI 生成个性化的旅行行程——逐日计划,包含活动、餐厅、酒店以及预算细分。它支持 英语、西班牙语和意大利语。我之所以创建它,是因为和朋友们一起规划团体旅行总是混乱,我想要更智能的方案。像往常一样,我最初是为自己构建的,但这一次我不想让它最终沦落到庞大的“项目墓地”。

应用本身运行良好。找到它的用户都很喜欢。

问题是什么? 没有人能找到它。

我必须弄清楚!而这仅仅是个开始。

第一阶段:“为什么 Google 没有显示我的站点?”

当我第一次查看 Google Search Console 时,我本以为会看到……什么东西。我已经上线好几周了。结果却是一条平直的线。

零展示。零点击。零已索引页面。

我的第一反应是责怪 Google。“需要时间,”我对自己说。于是又等了一周。仍然是零。

这时我真正检查了自己的设置:

  • ❌ 过时的站点地图
  • ❌ 没有规范标签(canonical tags)
  • ❌ 没有 hreflang 标签(尽管有 3 种语言)
  • ❌ 缺少结构化数据(structured data)
  • ❌ 来自 create-next-app 的默认 robots.txt
  • ❌ 一半页面没有 meta 描述

基本上,我建了一座漂亮的房子,却忘了在门上贴号码。让我们把注意力放在 SEO 上,而不是我的糟糕决定上吧。 😄

Source:

Phase 2: The Foundations (Boring but Necessary)

我花了一个周末来添加基础功能。没有什么革命性的东西,只是每个站点都需要的基本要素。

Sitemap

Next.js 用 app/sitemap.ts 让这件事变得很简单。我的站点会为所有静态页面、博客文章以及三个语言版本的目的地页面生成 URL。来自 Supabase 的动态内容也会被包含进来。

// Simplified version of my sitemap
export default async function sitemap(): Promise {
  const baseUrl = "https://monkeytravel.app";
  const locales = ["en", "es", "it"];

  // Blog posts × 3 languages
  const blogSlugs = getAllSlugs();
  const blogPages = blogSlugs.flatMap(slug =>
    locales.map(locale => ({
      url:
        locale === "en"
          ? `${baseUrl}/blog/${slug}`
          : `${baseUrl}/${locale}/blog/${slug}`,
      changeFrequency: "monthly" as const,
      priority: 0.7,
    }))
  );

  return [...staticPages, ...blogPages, ...destinationPages];
}

Canonical + Hreflang

多语言站点需要同时提供规范(canonical)URL 和一组备用语言 URL。我在每个页面中使用 generateMetadata()

alternates: {
  canonical:
    locale === "en"
      ? `${BASE_URL}/${slug}`
      : `${BASE_URL}/${locale}/${slug}`,
  languages: {
    en: `${BASE_URL}/${slug}`,
    es: `${BASE_URL}/es/${slug}`,
    it: `${BASE_URL}/it/${slug}`,
    "x-default": `${BASE_URL}/${slug}`,
  },
},

Structured Data

为 Organization、WebSite、SoftwareApplication、Article 和 TouristDestination 编写 JSON‑LD schema。我为此构建了一个小工具:

export function jsonLdScriptProps(data: object) {
  return {
    type: "application/ld+json",
    dangerouslySetInnerHTML: {
      __html: JSON.stringify(data),
    },
  };
}

Result

提交站点地图后,Google 在 48 小时内发现了我的所有 URL。但“发现” ≠ “已索引”。大多数页面停留在 “Discovered — currently not indexed”(已发现 — 当前未索引)队列中。

一周后:12 个页面已索引。有进展,但进展非常缓慢。

真正的问题是?Google Search Console 并没有提供太多关于页面被拒的原因的反馈。

第三阶段:Google 实际想要的内容

我意识到我的站点大部分是登录墙后面的应用。Google 能够索引的公开内容非常少,于是我在内容上大力出击。

  • 50 篇博客文章,涵盖真实的旅行主题、行程指南、目的地对比、预算旅行技巧、季节性推荐。
    × 3 种语言 = 150 个博客页面

  • 20 个目的地着陆页(巴黎、东京、巴厘岛、巴塞罗那等),提供气候数据、AI 行程预览,并交叉链接到博客文章。
    × 3 种语言 = 60 个页面

  • 5 个 SEO 着陆页,针对特定搜索意图:/free-ai-trip-planner/group-trip-planner/budget-trip-planner 等。

**让我惊讶的是:**内部链接的作用比内容本身更重要。那些被多个其他页面交叉链接的页面,索引速度 远快于孤立页面。于是我添加了:

  • 在每个着陆页上加入 “来自博客” 区块
  • 在每个目的地页面上加入 “相关目的地” 区块
  • 博客 ↔ 目的地链接(双向)
  • 在博客索引页添加地区筛选(欧洲、亚洲、美洲、非洲)

两周后:78 个页面已被索引。 索引曲线在加速上升。

Source:

第四阶段:几乎毁掉一切的 Bug

然后是 Google Search Cons…

(故事继续…)

未使用用户选择的规范链接的重复内容

Google 正在拒绝我的首页。它把 www.monkeytravel.app 作为规范链接,而不是 monkeytravel.app,尽管我已经:

  • 在中间件 以及 Vercel 配置中都做了 www → non‑www 的 301 重定向
  • 在 HTML 中使用了正确的规范标签
  • 在站点地图中所有 URL 都使用了非 www 形式

我仔细检查了一遍。重定向生效,HTML 中有正确的标签,curl 也证实了:

$ curl -s https://monkeytravel.app/ | grep canonical

那为什么 Google 会显示 “User‑declared canonical: None” 呢?

发现

我盯着这个问题看了好几个小时才恍然大悟。关键在于 我验证的方式

  • curl 会等待完整的响应返回。
  • Googlebot 不会

Next.js 15.2+ 中,generateMetadata()异步 流式输出元数据。<link rel="canonical"> 标签并不在最初的 HTML 负载中;它们是通过流在正文开始渲染后才注入的。当 Googlebot 解析初始响应时,规范标签根本不存在(至少这是我在翻阅 AI 回答和文档后得出的结论)。

我通过查看流式传输完成前的原始初始 HTML 进行确认——里面根本没有 <link rel="canonical">

解决方案:一个配置选项

// next.config.ts
const nextConfig: NextConfig = {
  // 为爬虫禁用流式输出,使完整的 HTML(包括元数据)同步发送
  htmlLimitedBots: /Googlebot|Google-InspectionTool|Bingbot|Yandex/i,
  trailingSlash: false,
};

export default nextConfig;

htmlLimitedBots 告诉 Next.js:“当爬虫访问时,禁用流式输出,同步发送包含所有元数据的完整 HTML。”
这一个正则表达式就解决了整个问题。

我还把根布局的规范链接从 "/" 改成了 "./",这样每个页面都会得到 自引用 的规范链接,而不是所有页面都指向首页(这是一个细微但重要的区别)。

部署并请求重新索引后,结果很快显现:

  • 176 个页面在几天内被索引
  • 虽然还不到全部 230+ 页面,但趋势已经非常明确。

数字

指标第 0 周第 1 周第 2 周第 3 周
已索引页面01278176
站点地图中的页面总数0~50~225~230
博客文章(每种语言)011550
结构化数据模式0355

我犯的错误(让你免于犯同样的错误)

  1. htmlLimitedBots 从第一天起未设置 – 每个关注 SEO 的 Next.js 项目都应该配置它。元数据流是静默的;手动检查时看起来没问题,但爬虫看到的情况却不同。
  2. 把 SEO 当作“以后再说”的问题 – 推迟生成站点地图和规范标签会每次浪费一周的潜在爬取时间。因为你急于求成,Google 的排队并不会加快。
  3. 低估内部链接的作用 – 交叉链接的页面比孤立页面的索引速度快 3‑4 倍。尽可能为相关内容添加链接。
  4. 缺少 hreflang 导致多语言支持不足 – 三个语言版本没有 hreflang,导致 Google 将它们视为重复内容而非翻译。

有帮助的 AI 技巧

  • AI 辅助的博客内容 – 使用 AI 起草结构,然后进行编辑和本地化。对于 50 篇文章 × 3 种语言,这节省了数月的人工工作。
  • 自动交叉链接 – 编写了一个脚本,分析主题并建议内部链接,效率远高于手动映射。
  • 面向国际化的提示工程 – 我没有直接翻译英文文章,而是让 AI 为目标受众 撰写(例如,“为意大利受众写一篇关于巴黎的文章”),从而产生更自然的内容。

我会对过去的自己说

在第 1 天就开始做这些事,别等到写完任何功能后再去做:

  • htmlLimitedBotsnext.config.ts
  • 站点地图生成
  • 每个页面都加上规范标签(canonical)
  • 将站点提交到 Google Search Console

其他的——博客文章、结构化数据、内部链接——也很重要,但这四项是基础。跳过它们,后面的所有工作都无从谈起。

更新: 仍有 129 个页面在 Google 的队列中。按照目前的进度,它们将在几周内被收录(希望如此)。随后真正的挑战就来了:在竞争激烈的关键词上获得排名。那将会 很有趣

MonkeyTravel 免费使用 —— 输入目的地,几秒钟内即可获得个性化的 AI 行程。使用 Next.js、Supabase 构建,部署在 Vercel。欢迎提供任何反馈!让我们一起学习新东西。

0 浏览
Back to Blog

相关文章

阅读更多 »

JavaScript的秘密生活:观察者

Timothy靠在椅子上,聆听笔记本电脑风扇突然而激进的嗡嗡声。他刚刚完成了一个 lazy‑loading 功能的实现,用于一个 mas...