创见博客
从用户协议排第一到首页 SEO 优化:一次内容站首页改造实践
七崽爱吃小饼干2026/07/25阅读 20专栏 Next.js/SEO

经过上一轮 SEO 优化后,Visionary 已经能在 Google 搜索结果中被找到。直接搜索“创见”或“visionaryblog”时,由于关键词本身竞争比较多,短期内还不容易稳定排到前面;但搜索“创见博客”时,我们的网站已经排到了第一。

不过这次出现了一个新的问题:排在第一的不是网站首页,而是“用户协议”页面。

在google搜索创见博客的结果
搜索visionaryblog,在第四页找到了

这篇文章记录这次的分析和优化过程:为什么用户协议会比首页更容易被搜索到,以及我们如何通过首页 metadata、canonical、真实内链和 ISR 首屏数据,让首页更像一个搜索引擎能理解的“品牌主页”。

问题现象

搜索“创见博客”时,Google 能识别到我们的站点,但第一条结果是:

text
创见博客网站用户协议

而不是:

text
创见博客首页

这说明网站已经具备一定的品牌相关性,但 Google 对不同页面的理解还不够准确:它认为“用户协议页”和“创见博客”这个查询词的匹配度更高。

为什么用户协议会排在首页前面

这类问题通常不是单一原因造成的,而是几个信号叠加后的结果。

1. 用户协议页天然包含大量品牌词

用户协议里通常会反复出现站点名称,例如:

text
创见博客网站用户协议
欢迎您使用创见博客网站
创见博客网站运营方

从搜索引擎的角度看,这个页面的标题和正文都强匹配“创见博客”。如果首页只叫“创见”,正文首屏又没有足够多可抓取内容,用户协议页就可能被认为更相关。

2. 首页 title 和 description 信号偏弱

优化前首页的 metadata 比较简单:

ts
title: "创见"
description: "Visionary"

这个写法对用户来说没问题,但对搜索引擎来说信息量不足。它没有明确告诉搜索引擎:

  • 这是“创见博客”的首页
  • 这是一个面向开发者的技术内容社区
  • 站点主要内容包括前端、后端、AI、工程实践等技术文章

3. 首页核心文章列表是客户端 runtime 获取

首页之前是一个 Client Component,文章列表在浏览器运行时通过接口获取:

ts
useEffect(() => {
  void getPublishedArticleList({ isInit: true, sort: currentTab });
}, [getPublishedArticleList, currentTab]);

这意味着初始 HTML 里主要是导航、Tab 和 Skeleton,真正的文章标题、摘要、标签等核心内容要等浏览器执行 JS 后才出现。

搜索引擎当然可以执行 JS,但对于内容站来说,首屏 HTML 直接包含核心内容,通常更稳定,也更容易让搜索引擎快速理解页面主题。

4. Logo 只是 JS 跳转,不是真实链接

导航 Logo 原来是通过点击事件跳转首页:

tsx
<span onClick={() => router.push('/')}>
  <LogoIcon />
  <span>创见</span>
</span>

这对用户点击没有问题,但从页面结构上看,它不是一个标准的 <a href="/"> 链接。搜索引擎更容易理解真实链接,所以品牌入口更适合用 next/link 输出真实锚点。

为什么这次没有直接给用户协议加 noindex

最直接的处理方式是给用户协议页加:

html
<meta name="robots" content="noindex, follow" />

这样 Google 后续会把用户协议从索引中移除,首页自然更容易成为“创见博客”的主结果。

但这次我们先没有这么做,原因是:用户协议本身也不是错误页面,它可以被搜索引擎收录;当前更核心的问题是首页信号太弱。相比“让用户协议消失”,这轮优化优先解决的是“让首页更像首页”。

所以这次策略是:

text
保留用户协议可索引
增强首页品牌和内容信号
让搜索引擎更容易把首页识别为创见博客的主入口

优化一:首页 title 和 description

首页 title 改成了更明确的品牌首页表达:

ts
title: "创见博客 - 面向开发者的技术内容社区"

description 改成了更完整的站点定位:

ts
description: "创见博客是面向开发者的技术内容分享与交流平台,聚合前端、后端、AI、工程实践等内容,帮助开发者沉淀经验、分享知识并持续成长。"

这两个字段解决的是“首页到底是什么”的问题。它们比单独的“创见”和“Visionary”更具体,也更符合用户搜索“创见博客”时想找到的页面。

给首页优化了title、description以及添加了canonical

优化二:首页 canonical 指向标准域名

我们给首页增加了 canonical:

ts
alternates: {
  canonical: "https://visionaryblog.cn",
}

生成后的 HTML 类似:

html
<link rel="canonical" href="https://visionaryblog.cn" />

canonical 的作用是告诉搜索引擎:当前页面的标准地址是哪个。配合之前做过的 www 到非 www 的 301 跳转,可以避免权重在下面这些地址之间分散:

text
https://visionaryblog.cn/
https://www.visionaryblog.cn/
http://visionaryblog.cn/
http://www.visionaryblog.cn/

需要注意的是,canonical 不能全站都指向首页。首页指向首页,文章页应该指向自己的文章 URL。比如文章 417 的 canonical 应该是:

html
<link rel="canonical" href="https://visionaryblog.cn/reader/417" />

所以这次也给文章详情页补了自己的 canonical,避免文章页被错误归并到首页。

优化三:Logo 改成真实首页内链

导航 Logo 从原来的 JS 点击跳转,改成了真实链接:

tsx
<Link href="/" aria-label="创见博客首页" className={styles.logo}>
  <LogoIcon />
  <span className={styles.logoText}>创见博客</span>
</Link>

这个改动很小,但对搜索引擎理解站内结构有帮助。

它传达了几个信号:

  • Logo 是首页入口
  • 链接文本包含“创见博客”
  • 站内品牌词统一指向首页

对于品牌词搜索来说,内链锚文本是一个很自然的相关性信号。

优化四:首页首屏数据改为 ISR

这次最重要的改动,是把首页从“完全客户端 runtime 获取文章列表”,改成“首屏文章由服务端生成,后续交互仍由客户端获取”。

改造后的结构是:

text
src/app/page.tsx        Server Page,负责 metadata、canonical、首屏文章数据和 ISR
src/app/HomeClient.tsx  Client Component,负责 Tab、加载更多、用户信息等交互

首页 Server Page 中直接获取最新文章第一页:

ts
const result = await getPublishedArticleList(0, 8, 'new');

然后把数据传给客户端组件:

tsx
return <HomeClient initialArticles={initialArticles} />;

同时开启 ISR:

ts
export const revalidate = 300;

这表示首页会静态生成,并且最多每 5 分钟重新验证一次。

首页优化为了ISR页面,html会带上第一批最新文章的数据

为什么 ISR 比纯客户端请求更适合首页

对于内容站首页来说,ISR 很适合“最新文章列表”这种数据:

  • 数据需要更新,但不需要每次请求都实时查询数据库
  • 首页访问频率通常比较高,静态缓存能降低服务器压力
  • 首屏 HTML 能直接包含文章标题、摘要和标签,对 SEO 更友好
  • 客户端仍然可以接管后续交互,比如切换热门、滚动加载更多

改造后的首页加载模式变成:

text
首次 HTML:包含首页 metadata、canonical、导航和最新 8 篇文章
Hydration 后:客户端接管 Tab、加载更多、用户状态等交互
后续分页:继续请求 /api/articles?pageNum=1&pageSize=8&sort=new
热门 Tab:点击后再请求 /api/articles?pageNum=0&pageSize=8&sort=hot

这样既保留了动态体验,又让搜索引擎不用等 JS 执行就能看到首页核心内容。

避免客户端重复请求第一页

引入服务端首屏数据后,还需要处理一个细节:客户端组件挂载后不要立刻再次请求第一页。

如果不处理,页面虽然 SSR/ISR 出了文章列表,但浏览器一执行 JS,又会调用一次:

text
/api/articles?pageNum=0&pageSize=8&sort=new

这会造成不必要的请求,也可能让页面出现短暂闪烁。

因此客户端组件用初始数据初始化状态,并在默认“最新”Tab 首次挂载时跳过重复请求:

ts
const { articleList, ... } = useGetPublishedArticleList(initialArticles);

useEffect(() => {
  if (!didUseInitialArticlesRef.current && currentTab === 'new' && initialArticles) {
    didUseInitialArticlesRef.current = true;
    return;
  }
  void getPublishedArticleList({ isInit: true, sort: currentTab });
}, [getPublishedArticleList, currentTab, initialArticles]);

后续切换 Tab、重试和加载更多仍然走客户端请求。

这次优化后的效果

这轮优化后,首页具备了更清晰的 SEO 信号:

  • title 明确包含“创见博客”
  • description 明确说明它是面向开发者的技术内容社区
  • canonical 指向标准首页地址
  • Logo 内链使用真实 <a> 链接,并以“创见博客”作为锚文本
  • 首页 HTML 直接包含最新文章列表,而不是只有 Skeleton
  • 文章详情页也有自己的 canonical,避免和首页信号混淆
  • www 域名已经 301 到非 www,统一站点权重

从构建结果看,首页已经成为 ISR 页面:

text
/  Revalidate 5m

这说明首页不是每次请求都重新 SSR,而是按 5 分钟周期做增量静态再生成。

后续还可以继续做什么

这次没有直接处理用户协议页索引,但后续可以继续观察 Search Console 里的表现。如果一段时间后“创见博客”搜索结果仍然优先展示用户协议,可以考虑进一步优化:

  1. 给用户协议页加更普通的 title,例如“用户协议”,减少品牌词权重。
  2. 给用户协议页加 noindex, follow,让它退出搜索结果但保留链接跟踪。
  3. 首页增加一个简短的品牌介绍区,用 H1 明确写出“创见博客”。
  4. 给专栏页、作者公开页继续补 canonical。
  5. 发布文章时主动 revalidatePath('/'),让首页 ISR 缓存更快更新。

SEO 优化不是一次性动作。对于内容站来说,更重要的是持续让搜索引擎看到稳定、清晰、可抓取的内容结构:品牌主页负责承载品牌词,文章页负责承载长尾关键词,站内链接负责把它们连接起来。

这次改造的核心目标,就是让“创见博客”的首页真正成为搜索引擎眼里的主入口。

后续更新

经过之前的改造,现在关键词“创见博客”中第一个展示的条目已经成功变成创见首页,不过title和description好像还不太对,可能抓取的是首页SSR改造之前的内容

另外关键词“visionaryblog”中第二页第一条完整展示了我们网站的title和description,排名从之前的第四页直接上升到了第二页。

7-25

经过优化,网站排名逐渐提升,搜索visionaryblog和创见博客,第一条就是visionaryblog.cn
在百度的搜索排名也排到了第一
评论
0/100