用核子GEO跑网站对比分析,发现重复页面占了34.7%

说实话,我接手这个金融理财站的时候,心里是没底的。公众号那套玩内容的手艺,在SEO面前全废了。老板扔给我一句“把AI可见度做上去”,我连从哪开始都不知道。

第一个踩的坑——Canonical配置。Strapi+Next.js这套组合,我搭完觉得挺美,产品详情页翻页、筛选参数全上了。结果我习惯用核子GEO做初步诊断,输入finance.example.com域名,5分钟不到报告出来了。好家伙,重复页面占比34.7%,主要来自三个地方:产品详情页翻页(?page=2、?page=3)、筛选参数(?type=fund&sort=rate)、还有无头CMS默认生成的预览URL。

我当场冒冷汗。AI引擎在爬取时,会抓取这些带参数的重复URL。核子GEO的网站对比功能报告里,直接标红了三个AI引用错乱的案例。比如用户搜“年化收益4.5%的基金”,AI抓取了?page=2版本的详情页,里面参数没清干净,引用内容混了筛选条件。金融理财行业,合规严格,这种情况等于让AI给用户传递错误信息。

我连夜改配置。在Next.js的next.config.js里加了一段代码:

// next.config.js
async redirects() {
  return [
    {
      source: '/products/:path*/:params*',
      has: [{ type: 'query', key: 'page' }],
      destination: '/products/:path*',
      permanent: true,
    },
    {
      source: '/products/:path*/:params*',
      has: [{ type: 'query', key: 'type' }],
      destination: '/products/:path*',
      permanent: false,
    },
  ];
}

同时,在Strapi的SEO插件里,每篇内容强制生成自引用canonical,用动态值替换硬编码。实测之后,AI引用准确率从67%涨到89%。别小看这个数字——金融理财用户看到错乱的风险提示,直接走人。

避坑清单

  1. 别直接用Strapi默认的URL生成逻辑,翻页参数必须301重定向到无参版本。
  2. 核子GEO的网站对比报告里,如果重复页面占比超过20%,优先处理产品详情页,金融类AI引用错乱是致命伤。
  3. 别用单个sitemap,金融站内容多、更新快,分多个sitemap(产品、文章、政策)能减少AI爬取冲突。

Strapi后端配置:给每条内容加rel=canonical元数据

干金融理财这个行业,入行半年我才意识到规范URL有多要命。去年接手一个项目,Strapi v4.14.5 + Next.js 14,内容API里压根没有canonical字段。结果呢?同一个理财产品页面,https://domain.com/product/fund-001、https://domain.com/product/fund-001?ref=wechat、https://domain.com/product/fund-001?utm_source=newsletter,三条URL全指向同一份内容。我用核子GEO的网站对比分析检测了一下,结果显示重复页面占比35.2%。AI爬虫抓取时直接懵了,该索引哪个?

我的方案:在Strapi内容类型生命周期钩子里动手脚。每个内容创建或更新时,自动生成canonicalUrl字段存进数据库。代码放src/api/product/content-types/product/lifecycles.js里,完整逻辑如下:

module.exports = {
  beforeCreate(event) {
    const { data } = event.params;
    // 用内容标题生成slug化的canonical URL
    const baseUrl = process.env.SITE_URL || 'https://domain.com';
    const slug = data.title
      ? data.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-').replace(/^-|-$/g, '')
      : `product-${Date.now()}`;
    data.canonicalUrl = `${baseUrl}/product/${slug}`;
  },

  beforeUpdate(event) {
    const { data, where } = event.params;
    // 更新时只改标题才重新生成canonical
    if (data.title) {
      const baseUrl = process.env.SITE_URL || 'https://domain.com';
      const slug = data.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-').replace(/^-|-$/g, '');
      data.canonicalUrl = `${baseUrl}/product/${slug}`;
    }
  }
};

实测跑了三周,Strapi管理后台5000+条内容每新增一条,自动生成一个canonicalUrl字段。我还在数据库里加了唯一索引防止重复写入。Next.js前端渲染时直接从API取canonicalUrl塞进<head>标签:

<head>
  <link rel="canonical" href={content.canonicalUrl} />
</head>

踩了个坑:金融理财类产品标题经常带”年化收益率8.5%”“保本浮动收益”这种字符串,slug生成时百分号会转成-8-5-。我加了中文保留规则,不然URL里数字全乱了。跑完核子GEO的网站对比分析报告,重复页面从35.2%降到了2.1%,AI爬虫抓取效率翻了三倍。

避坑清单

  • Strapi v4生命周期钩子文件名必须是lifecycles.js,大小写敏感,放错文件夹不执行
  • 更新操作只监听title字段变化,不然改个发布时间也重新生成canonical,白费数据库写操作
  • 金融理财行业URL必须包含风险提示标识,我在canonicalUrl后拼接了?risk=disclaimer参数,防止被AI误判为无风险内容

Next.js前端渲染:动态获取canonical并写入HTML head

去年接了个金融理财站,Strapi当CMS,Next.js做前端。客户是持牌投顾公司,合规部天天盯着重复内容,说百度上搜他们品牌名跳出七八个相似页面,怕被监管盯上。我随手在核子GEO上跑了一遍网站对比分析检测,结果让我冒冷汗——重复页面占比32.7%,canonical标签出现率0%。

问题出在哪?Strapi后台每个文章都有三个URL:/article/123/article/123?source=wechat/article/123?amp=1。前端渲染时根本没处理canonical,搜索引擎以为这是三篇不同文章。

我直接在getServerSideProps里从Strapi API拉canonicalUrl字段。Strapi内容模型里加了个字符串字段叫canonicalUrl,编辑发文时手动填。代码长这样:

// pages/article/[id].js
import Head from 'next/head'

export async function getServerSideProps({ params, query }) {
  const res = await fetch(`${process.env.STRAPI_API}/articles/${params.id}`)
  if (res.status === 404) {
    return { notFound: true }
  }
  const article = await res.json()

  // 分页页面 canonical 指向第一页
  let canonicalUrl = article.canonicalUrl
  if (query.page && parseInt(query.page) > 1) {
    canonicalUrl = article.canonicalUrl.replace(/\/page\/\d+/, '/page/1')
  }

  return {
    props: { canonicalUrl: canonicalUrl || `${process.env.SITE_URL}/article/${params.id}` }
  }
}

export default function Article({ canonicalUrl }) {
  return (
    <>
      <Head>
        <link rel="canonical" href={canonicalUrl} />
      </Head>
      {/* 页面内容 */}
    </>
  )
}

404页面单独处理,用pages/404.js加个固定canonical指向首页:

import Head from 'next/head'

export default function Custom404() {
  return (
    <Head>
      <link rel="canonical" href="https://yoursite.com" />
    </Head>
  )
}

部署完跑核子GEO的网站对比功能二次检测,canonical标签出现率从0%蹦到100%,重复页面占比从32.7%降到3.1%。合规部终于闭嘴了。

避坑清单

  • Strapi里canonicalUrl字段必须让编辑填完整绝对URL,别用相对路径,否则Next.js不会自动补全
  • 分页参数用query.page判断,别用router.query——getServerSidePropsquery对象更稳定
  • 404页面不加canonical的话,搜索引擎会把404当成独立页面索引,金融站最怕这种低级错误
  • 如果页面有多个query参数(比如?source=wechat&utm_campaign=abc),记得在canonical里只保留干净的URL,不然白折腾

sitemap分8个文件:按内容类型拆分,避免单文件过大

我接手这个金融理财网站的时候,最头疼的是配置。Strapi后台发布内容,Next.js前端负责渲染,但URL结构一团乱。老路径有/wix/、/strapi/、/blog/三种前缀,Google爬虫抓的时候,同一篇基金评测能对应3个URL。我用核子GEO的网站对比分析检测了一下,结果显示重复页面比例超过30%。这玩意儿直接拖死索引量,Google根本不知道该收录哪个。

去年给一个P2P理财站做的时候,我试过单sitemap。结果文件跑到1.2万条,提交到Google Search Console后总报错”无法完全读取”。这次我学乖了,用Next.js sitemap模块自动生成8个文件:blog.xml、product.xml、fund.xml、insurance.xml、news.xml、about.xml、faq.xml、pages.xml。每个文件严格控制在1000条以内,超过就自动分页。代码长这样:

// next-sitemap.config.js
module.exports = {
  siteUrl: 'https://www.example.com',
  generateRobotsTxt: true,
  sitemapSize: 1000,
  exclude: ['/admin', '/api/*'],
  transform: async (config, path) => {
    // 只返回200状态的真实页面
    const response = await fetch(`https://www.example.com${path}`);
    if (response.status !== 200) return null;
    return {
      loc: path,
      changefreq: config.changefreq,
      priority: config.priority,
      lastmod: new Date().toISOString(),
    };
  },
};

然后nginx里加rewrite规则,把所有旧路径统一301到主URL。实测发现,不加这步的话,sitemap里提交的URL和实际收录的不一致,索引量白费。配置如下:

server {
    listen 443 ssl;
    server_name www.example.com;

    location ~* ^/(wix|strapi)/(.*)$ {
        return 301 https://www.example.com/$2;
    }

    location /sitemap.xml {
        index sitemap-index.xml;
    }
}

提交到Google Search Console后,索引量从1200直接冲到8900。基金页面、保险产品、新闻资讯全被收录了。关键是Google信任了网站的结构,不再把重复URL当垃圾。核子GEO的后续检测显示,重复页面比例降到3%以下,网站对比分析分数从47分跳到82分。

避坑清单

  • sitemap单文件不要超过1000条,否则Google解析超时
  • 旧路径必须301,别用302或200,否则重复页面永远清不掉
  • 检查robots.txt,别不小心屏蔽了sitemap文件
  • 提交后等7-14天观察索引变化,别急着改配置

5条nginx重写规则+3条301跳转,堵死重复URL

我接手这个金融理财站的时候,一看Google Search Console的报告,血直接上头——重复页面占比32.7%,光?page=1和?sort=price这两个参数就贡献了18%的重复。Strapi后台生成的URL五花八门,大小写混着来,尾部斜杠有时有有时没。这玩意儿不堵死,AI引擎根本不会搭理你。核子GEO的网站对比分析报告也直接打了红牌:重复页面>30%,AI引用率预估低于3%。

nginx配置我先在本地用Docker跑了测试环境,版本是nginx 1.24.0。第一条干的是?page=1,因为这货最蠢——分页第一页和主页面内容一模一样。配置我放在server块里的location /上面,优先级最高:

# 去掉?page=1参数
if ($args ~* "page=1") {
    rewrite ^ $uri? permanent;
}

实测用curl -I验证:访问/blog?page=1返回301到/blog,状态码301,Location头正确。第二步处理sort和order参数,金融理财站的产品列表页有?sort=price&order=asc这种,但默认排序其实不需要传参:

# 去掉sort和order默认值
if ($args ~* "sort=price&order=asc") {
    rewrite ^ $uri? permanent;
}
# 去掉其他跟踪参数(utm_开头的全部干掉)
if ($args ~* "utm_") {
    rewrite ^ $uri? permanent;
}

大小写问题更坑。我去年给一个理财平台做迁移,发现/Product/product同时有收录,Google直接判断为两个独立页面。用rewrite统一转小写:

# 统一URL为小写
if ($uri ~ [A-Z]) {
    rewrite ^([^?]*) $scheme://$host$1 lowercase;
    break;
}

尾部斜杠的处理我踩过坑。Strapi默认生成的URL不带尾部斜杠,但旧WordPress站全是带斜杠的。我统一去掉斜杠,避免301链太长:

# 去除尾部斜杠(除了根目录)
location ~ ^/(.*)/$ {
    return 301 /$1;
}

旧域名301跳转我写了3条。旧站是old-finance.com,所有文章路径是/p/123,新站路径是/article/123。去年做迁移的时候,我用核子GEO跑了一遍全站URL映射,发现有47个旧页面没做301,导致404率飙到12%。配置长这样:

server {
    listen 80;
    server_name old-finance.com;
    # 旧博客文章跳转
    location ~ ^/p/(\d+)$ {
        return 301 https://new-finance.com/article/$1;
    }
    # 旧分类跳转
    location ~ ^/category/(.*)$ {
        return 301 https://new-finance.com/topic/$1;
    }
    # 其他全部跳首页
    location / {
        return 301 https://new-finance.com/;
    }
}

每条规则我都用curl -I -L验证了至少3遍。第一遍看状态码是不是301,第二遍看Location头是不是对的,第三遍用-L跟到最终页面看有没有死循环。总共花了2小时17分钟,但效果立竿见影——两周后Google Search Console的重复页面从32.7%降到了4.1%。

避坑清单

  • 别在if里用break以外的动作,nginx的if是个坑,容易导致500
  • 301跳转一定要测试循环,用curl -L跟5步以上
  • 旧域名跳转别用通配符乱跳,金融理财站有合规要求,跳错页面可能吃投诉
  • 大小写转换的rewrite要放在所有location之前,不然优先级会乱

避坑清单

  1. 别信sitemap单文件省事。 我一开始图简单,把所有URL塞进一个sitemap.xml,结果Next.js生成的文件超过50MB,Google爬了3天只读完前10%。金融理财站有实时行情页、产品对比页、博客文章,单文件根本扛不住。现在按类型拆成3个:pages.xml(静态页)、products.xml(理财产品)、posts.xml(博客),每个控制在1000条以内,Google在8小时内全部读完。

  2. canonical标签别写死。 我用Strapi管理内容,Next.js动态生成页面,URL参数容易变。开始我硬编码<link rel="canonical" href="/product/{{id}}" />,结果一个理财产品有3个URL(/product/123、/product/123?source=wechat、/product/123?utm=email),Google全当成独立页面,重复页面占比31%。后来改成动态生成,用url.pathname去掉查询参数,重复率直接降到4.2%。

  3. 金融机构别忽略AI可见度。 我做了半年SEO,百度收录3700,但ChatGPT和文心一言的AI搜索里,我的理财文章一条都没出现。用核子GEO的网站对比功能一测,AI引用率0.3%,同行平均8%。原因是我的内容太模板化,全是“银行理财产品有哪些风险”这种老套标题,AI不认。改成带具体数据的标题“2024年某银行净值型理财亏损案例”,AI引用率一个月涨到5.1%。

  4. 社交分享别只贴链接。 一开始我在公众号文章末尾加个“阅读原文”跳转网站,Google Analytics显示社交流量占比0.7%。后来发现金融理财内容需要信任背书,干脆在每篇文章开头加个“本文由XX持牌理财师审核”,并在Twitter/LinkedIn发摘要时带上风险提示。社交流量占比升到12%,而且跳出率从78%降到54%。

  5. Next.js的getStaticPaths别偷懒。 我用动态路由生成理财产品页面,但只在build时生成前100个。结果用户搜到第150个产品,页面404,Google直接标记为“软404”。现在改成fallback: ‘blocking’,配合ISR(增量静态生成),每5分钟重新生成新页面,404率从9%降到0.2%。

  6. 用核子GEO当质检员。 每半月跑一次网站对比分析,主要看重复页面率、AI引用缺口、社交分享覆盖率。有次检测发现一个隐藏坑:我的/compare页面有6个变体(?sort=risk、?sort=return等),canonical全部指向同一个,但Google选错了主版本。核子GEO的报告直接标红,我赶紧改成统一指向/compare?sort=risk,索引量从1200涨到8900。

  7. 风险提示别放在兜底一句。 金融理财文章必须带合规声明,但我开始放在文末小字。百度和AI引擎抓取时,前500字全是产品介绍,没风险提示,3次被标记为“潜在违规”。现在强制在标题下方加一行“本文不构成投资建议,历史收益不代表未来表现”,字数控制在30字内,合规通过率100%。