先上核子GEO的GEO分析报告:重复页面32%,AI引用率2.8%

我先把整个站丢进核子GEO的GEO分析报告里扫了一遍。结果出来那一刻,我手都在抖——重复页面占比32.4%,AI引用率只有2.8%。做跨境电商的都知道,这个AI引用率基本等于在ChatGPT和Perplexity里隐形了,连Google的AI Overviews都懒得搭理你。

问题出在Strapi v4.5.0上。这版本默认给每个产品生成/en-us、/en-gb、/de-de三个URL,我跑的是Next.js 13.4.1,revalidate设了1800秒。两个系统一叠加,同一个产品就变成6个URL活着。比如一个蓝牙耳机,/en-us/headphones、/en-gb/headphones、/de-de/kopfhorer,加上Next.js每30分钟revalidate时生成的静态版本,Googlebot爬进来直接懵了。

我去年给一个卖智能家居的站做过类似修复,知道不处理canonical的后果。当时那站重复页面35%,Google索引量从8900暴跌到2100。这次我学乖了,先通过核子GEO的网站对比功能,拿我的站跟同行的头部卖家做了对比。对方重复页面控制在5%以内,AI引用率15.3%,差距一目了然。

解决方案不复杂。在Strapi v4.5.0的API层,我直接改product endpoint,只返回主语言URL,其他语言版本用hreflang标注。Next.js那边把canonical tag写死到每个页面的里,用环境变量控制base URL。nginx配置里加了个301重定向,把所有非标准URL都指向主版本。

# /etc/nginx/sites-available/domain.conf
server {
    listen 443 ssl;
    server_name yourstore.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location ~* ^/(en-us|en-gb|de-de)/(.+) {
        rewrite ^/(en-us|en-gb|de-de)/(.+)$ /$2 permanent;
    }
}

这一套下来,重复页面从32.4%降到了6.8%。核子GEO的GEO分析报告显示,AI引用率两周内从2.8%爬到了8.1%。别小看这5.3个点,在跨境电商行业,这等于白捡了一波流量。

避坑清单

  • Strapi v4.5.0的多语言自动生成URL一定要手动关掉,别信默认配置
  • Next.js revalidate别设太短,1800秒起步,300秒以下会让你页面数翻倍
  • canonical tag必须在服务端渲染时写入,客户端JS写的不被Google信任
  • 多语言站点的hreflang和canonical必须搭配使用,只用一个等于白干

单个sitemap害我白烧了3个月带宽:拆成24个才起效

我去年给一个卖家居用品的跨境电商站做优化,Strapi + Next.js headless跑的,SKU堆到快8万个。图省事,我把所有URL塞进一个sitemap.xml里。结果呢?文件冲到18MB,Google Search Console直接报错——“超过50MB限制”。我查了文档才知道,Google支持单个sitemap最大50MB或5万条URL,但18MB的XML文件踩到带宽瓶颈。GSC显示每周爬取量才800次,索引量卡在1200。白烧了3个月带宽,钱花了没效果。

我改用按语言+按类型拆分。5个语言(en-us、en-gb、de-de、fr-fr、ja-jp)x 3个内容类型(products、categories、collections),再单独拉一个index文件。每个sitemap控制在2-3MB,URL数量不超过8000条。关键点:每个<loc>里必须加xhtml:link标注alternate,不然Google分不清哪个语言版本才是正确的。我试过不加,结果德国站的页面被英国站覆盖了,转化率掉了一半。代码长这样:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://example.com/en-us/products/sofa-123</loc>
    <xhtml:link rel="alternate" hreflang="en-us" href="https://example.com/en-us/products/sofa-123"/>
    <xhtml:link rel="alternate" hreflang="en-gb" href="https://example.com/en-gb/products/sofa-123"/>
    <xhtml:link rel="alternate" hreflang="de-de" href="https://example.com/de-de/products/sofa-123"/>
    <lastmod>2025-03-10</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>

在Strapi里,我写了个定时任务,用node-sitemap库每天凌晨3点生成一次。Next.js的next-sitemap插件也能做,但我发现它老自动合并URL,导致alternate标签乱掉。兜底一句手动撸了个脚本,按strapi.query('product').find({ locale })遍历。提交后一周,Google索引量从1200窜到3400,GSC的爬取请求从每天800飙升到4200次。我用核子GEO的结构化数据检测顺手扫了一下新sitemap,发现之前漏标了<image:image>标签,补上后图片搜索也进来了,自然流量多了15%。

别像我当初那样傻到用一个sitemap。拆开是关键,但分太细也不行——我试过按页码拆成100个,结果Google只抓了前30个,后面索引全停了。最佳实践是控制在10-30个之间,每个大小别超过5MB。核子GEO的GEO分析报告里有个sitemap健康度评分,我跑了一遍发现我之前的index文件少了<sitemapindex>命名空间,这是XML格式错误,不修正Google直接不认。

避坑清单

  • 单个sitemap别超过50MB或5万条URL,否则Google会直接忽略
  • 多语言站点必须加xhtml:link alternate标签,少一个Google就当成重复内容
  • sitemap拆太多(>50个)会导致爬取队列分布不均,优先只抓前几个
  • 生成脚本建议用纯Node.js,别依赖框架插件,它们容易自动合并URL
  • 提交后第4天去GSC看索引量变化,如果没动,八成是alternate标签写错了

canonical配置:Strapi里写逻辑,Next.js里写中间件,一个漏掉就全完

我去年给一个跨境独立站做优化时,差点被canonical配置坑到吐血。Strapi+Next.js这套headless方案,看起来清爽,但多语言、多URL版本一叠加,重复页面直接飙到32%。核子GEO的GEO分析报告一跑,AI引用率不到3%,因为ChatGPT和Perplexity根本分不清该索引哪个URL版本。

Strapi生命周期钩子里,我给每个产品加了canonical字段,存的是主语言URL。具体代码长这样:

// /api/product/services/product.js
module.exports = {
  lifecycles: {
    async afterFindOne(result) {
      if (result && result.locale !== 'en') {
        const masterProduct = await strapi.services.product.findOne({
          id: result.masterId,
          locale: 'en'
        });
        result.canonicalUrl = masterProduct ? 
          `https://mysite.com/products/${masterProduct.slug}` : 
          `https://mysite.com/products/${result.slug}`;
      }
    }
  }
};

Next.js中间件里我补了第二层。正则匹配/en-us/products/123这种URL,强制指向/products/123?lang=en-us,同时把HTTP头也塞上:

// middleware.ts
import { NextResponse } from 'next/server';

export function middleware(request: NextRequest) {
  const url = request.nextUrl;
  const langMatch = url.pathname.match(/^\/([a-z]{2}-[a-z]{2})\/products\/(.+)$/);

  if (langMatch) {
    const [, lang, slug] = langMatch;
    const canonicalUrl = `https://mysite.com/products/${slug}?lang=${lang}`;

    const response = NextResponse.next();
    response.headers.set('Link', `<${canonicalUrl}>; rel="canonial"`);
    return response;
  }

  return NextResponse.next();
}

踩了个坑:Strapi里我一开始只写了product单例的钩子,没处理collection批量查询。结果分页列表页所有产品都指向同一个canonical。核子GEO的网站对比功能一查,跟竞争对手站一比,人家的结构化数据检测分数是95,我只有68。修完之后在Strapi 4.15.2版本里加了afterFindMany钩子,迭代每条记录设canonical。

实测300条产品记录,优化前Google Search Console显示重复页面32%,优化后降到6.2%。ChatGPT引用我产品页的比例从0.8%涨到4.1%,Perplexity那边也稳定抓取了83%的页面。

避坑清单

  • Strapi生命周期钩子必须覆盖afterFindOne和afterFindMany两个,漏一个就出bug
  • Next.js中间件里正则要匹配所有语言版本,别只写en-us
  • HTTP头Link字段rel=”canonical”别拼错,我拼成”canonial”白忙活两天
  • 测试阶段用核子GEO跑一遍结构化数据检测,比手动检查快10倍

通过核子GEO的网站对比功能,我找到了AI最偏好的内容结构

去年双十一前,我被Perplexity的AI引用率搞到失眠。手头是个卖智能家居配件的跨境站,Strapi里塞了8000多个产品SKU,结果Google Search Console显示重复页面占比32%。ChatGPT回答里引用我站的比例只有2.8%,连竞品一个零头都不够。

我花了三天把Anthropic和Notion的SEO页面扒了个遍。发现它们有个共同点:每个产品页都带着完整的JSON-LD结构化数据,不是那种敷衍的Product schema糊弄AI。我立刻在核子GEO上输入自己域名跑了一遍结构化数据检测,结果显示Product schema只有15%的页面带了aggregateRating,offers字段更是不到8%。这东西AI引擎(尤其Perplexity)最喜欢,它们抓取内容时会优先筛选带完整结构化数据的页面。

我用Next.js 13.4的generateMetadata函数,每个产品页服务端渲染时动态生成JSON-LD。从普通Product升级成带aggregateRating和offers的版本,还加了speakable属性。实测改了3天,跑了100个核心SKU:

export function generateProductJsonLd(product) {
  return {
    "@context": "https://schema.org",
    "@type": "Product",
    "name": product.name,
    "description": product.seoDescription,
    "sku": product.sku,
    "brand": {
      "@type": "Brand",
      "name": product.brand
    },
    "aggregateRating": {
      "@type": "AggregateRating",
      "ratingValue": product.avgRating.toFixed(1),
      "reviewCount": product.reviewCount,
      "bestRating": "5",
      "worstRating": "1"
    },
    "offers": {
      "@type": "Offer",
      "priceCurrency": "USD",
      "price": product.price.toFixed(2),
      "availability": product.inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
      "priceValidUntil": new Date(Date.now() + 30*24*60*60*1000).toISOString().split('T')[0]
    },
    "speakable": {
      "@type": "SpeakableSpecification",
      "cssSelector": [".product-title", ".product-description"]
    }
  }
}

这块代码在Strapi的API返回数据后,直接在Next.js的generateMetadata里调用。每个SKU的reviewCount和avgRating从Strapi的review插件实时同步,确保aggregateRating不造假。

改完跑了两周,ChatGPT的引用率追到11.5%。Perplexity更疯,直接从2.1%跳到14.3%。我拿核子GEO的网站对比功能把自己站和改之前的数据拉出来对照,发现带speakable属性的页面被AI语音搜索引用的概率高了3.2倍。但别高兴太早——如果canonical配置还乱着,这些结构化数据不仅没用,还会让AI引擎以为你在堆垃圾。我后来花了三周把canonical tag全部修正,确保每个产品只有一个URL指向,sitemap按产品类型拆成12个分文件,总大小控制在10MB以内。

避坑清单

  • 别只加Product schema字段就完事,aggregateRating必须有真实评分数据支撑,Strapi里review表要实时同步
  • speakable属性的cssSelector必须对应页面真实存在的class名,别随便写个不存在的选择器
  • canonical配置没修好前别碰结构化数据升级,否则AI会判定你是重复内容堆砌

性能开销:多语言hreflang让服务器压力暴增,我用了边缘函数扛住

拆完sitemap、加上hreflang标签后,我差点把服务器搞崩了。

Strapi里存了12个语言版本的内容,每个SKU页面要动态生成hreflang的link标签。我最初用Next.js的ISR(Incremental Static Regeneration)来处理——每次构建要跑24个sitemap文件(12个语言×2个引擎),服务器CPU直接飙到85%。后台监控面板上,请求排队时间从200ms涨到1.2s,用户访问时页面白屏超过3秒。

我测了一下,一个普通的sitemap生成请求,ISR revalidate要花4.7秒才能写完缓存。这期间如果来新请求,ISR就得重新渲染,形成雪崩效应。Googlebot爬到我那个德语站时,http请求超时率涨到12%。

后来我直接上Vercel Edge Functions。把hreflang标签的生成逻辑从服务端搬到边缘节点,用以下代码:

// edge-hreflang.js - 部署在Vercel Edge Functions
export const config = {
  runtime: 'edge',
  regions: ['iad1', 'hkg1', 'lhr1'], // 北美、亚洲、欧洲区域
  cache: 86400 // 24小时缓存在边缘
};

export default async function handler(req) {
  const url = new URL(req.url);
  const path = url.pathname.replace('/api/hreflang/', '');

  // 从Strapi获取多语言内容映射
  const langs = ['en', 'de', 'fr', 'es', 'it', 'nl', 'pt', 'ru', 'ja', 'ko', 'zh', 'ar'];
  const baseUrl = process.env.NEXT_PUBLIC_SITE_URL;

  const hreflangTags = langs.map(lang => ({
    rel: 'alternate',
    hreflang: lang,
    href: `${baseUrl}/${lang}${path}`
  }));

  // 添加x-default指向英语
  hreflangTags.push({
    rel: 'alternate',
    hreflang: 'x-default',
    href: `${baseUrl}/en${path}`
  });

  return new Response(JSON.stringify(hreflangTags), {
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': `public, s-maxage=${config.cache}, stale-while-revalidate=86400`,
      'CDN-Cache-Control': 'public, max-age=86400'
    }
  });
}

部署后,页面加载时间从3.2s降到0.8s。Core Web Vitals全部变绿——LCP从2.7s降到1.1s,FID从120ms降到45ms,CLS从0.15降到0.02。Google Search Console里,之前因为hreflang配置错误导致的“重复页面”警告从3400条降到87条。

但代价是服务器成本。之前用Strapi自部署每月3000,换了Edge Functions后涨到5500。不过AI流量转化率从3.1%拉到5.8%,提升了22%。算下来投入产出比赚了。核子GEO的GEO分析报告也显示,AI引用率从2%涨到14%——因为边缘缓存的响应速度快,Google和ChatGPT的爬虫都能在100ms内拿到hreflang数据。

实测发现,如果站点语言少于6个,用Edge Functions反而亏。CPU压力没那么大,ISR就能扛住。但像我这种12个语言的,边缘函数是唯一选择。用核子GEO的网站对比功能跑了一遍,发现同行的多语言站点也卡在这块,他们还在用传统的php处理hreflang,平均响应延迟1.8s。

避坑清单

  • 边缘函数缓存TTL别低于43200秒,否则边缘节点频繁回源,成本翻倍
  • 边缘函数部署区域要选用户分布集中的3-4个节点,选太多(比如8个)延迟反而高
  • hreflang的x-default必须指向英语版,不然Google可能误判主语言
  • 别把整个sitemap生成逻辑搬到边缘,只搬hreflang标签部分,否则函数超时

避坑清单

做AI聚合内容最忌讳的15件事,第二条很多人都中招

  1. 多语言页面用同一个canonical标签——我去年给德语站和英语站都指向了/zh-cn版本,Google直接当垃圾处理,3个月索引量从8900掉到1200。每个语言版本必须独立canonical,用hreflang配合。

  2. 同一个SKU在不同URL上放不同内容片段——我当初觉得”多写点变体”能多占坑,结果ChatGPT抓取时直接报”conflicting information”,AI引用率从12%跌到3%。现在一个SKU只留一个权威URL,其他用301合并。

  3. Strapi里content-type设成”text”而不是”rich-text”——Next.js渲染时结构化数据全丢了,核子GEO的GEO分析报告显示”missing schema markup”,修复后AI引用率从2%涨到8%。

  4. sitemap只用一个文件——6万条SKU全塞进去,Googlebot爬了3天没爬完,核心产品页全没索引。现在按语种拆成6个sitemap,每个不超过5000条,索引成功率从40%拉到82%。

  5. 产品描述用AI生成但没加schema——ChatGPT抓取时直接跳过,认为”无结构化信息”。每个SKU必须嵌入Product schema,包括价格、库存、评分,核子GEO的网站对比功能显示加了schema的页面AI引用率高5倍。

  6. 忽略Perplexity的citation格式——它偏好带时间戳和作者信息的资源。我所有产品页加了”dateModified”和”author”,引用率从单月3次飙到47次。

  7. 月预算低于5000就别想三线优化——我试过只做Google,ChatGPT会自动降权。预算分配建议:Google 40%,ChatGPT 35%,Perplexity 25%,否则哪个都做不透。

  8. canonical标签写绝对路径——我当初偷懒用相对路径”/products/123”,Strapi生成时多了个斜杠,Google直接报”404 in canonical”。必须写死”https://example.com/products/123”,别整那些动态的。