一测文心搜索,发现canonical配置炸了:重复页面33.7%

去年底接手这个电商站,SKU 1.2万个,价格一天变三回,我心里其实有底——这种站出问题是常态。但真没想到canonical能烂成这样。在核子GEO上输入域名,选文心搜索的对比模式,结果报告弹出来那一下,我人傻了:重复页面占比33.7%。说实话,这数字比我预想的高了快一倍。

啥概念?就是每三个URL就有一个是没用的复制品。拿一个叫“夏季棉麻衬衫-男”的SKU举例,我手动查了一下,/product/shirt-1、/product/shirt-1?page=1、/product/shirt-1?sort=price_asc、还有Strapi后台自动生成的/product/shirt-1?variant=large,四个URL全被文心索引了。你说气不气?这还不算,关键是我用核子GEO的网站对比功能一挖,发现canonical标签要么压根没出现,要么指向了不同的版本。

根源在哪?查了一圈,Strapi后台装的SEO插件版本2.3.1,默认不生成canonical标签当时就懵了。Next.js路由又天生喜欢加?page=1这种参数,开发那会儿没人管这茬。我初步统计,光分页参数和排序参数产生的重复URL,占了总索引的22%。剩下的11%是Strapi内容API里variant参数搞的鬼。

我当时就想,这事儿不解决,文心搜索的信任度直接归零。重复页面多了,搜索引擎默认你站质量低,权重分配稀碎。现在想想,要是早点用核子GEO这样的工具扫一遍,不至于拖到重复占比超三成才发现。

修复Strapi+Next.js的canonical:在getServerSideProps里硬编码

Strapi 2.3.1的SEO插件有个隐藏功能——extendContentTypes。我直接在商品内容类型里加了canonical_url字段,类型选文本,默认留空。这样编辑商品时能手动指定标准URL,比如某个商品有颜色变体,统一指向主SKU。

Next.js那边就折腾了。在pages/product/[slug].tsx的getServerSideProps里,我从Strapi API取到canonical_url,然后传给head组件。head组件里判断逻辑很简单:如果canonical_url存在就用它,否则自动用当前URL去掉所有查询参数。别小看这个判断,之前没做这个兜底,很多页面直接没输出canonical标签,文心一言抓取时全当重复内容处理了。

next.config.js里我把trailingSlash设为false。这招是踩坑换来的——/product/shirt-1/和/product/shirt-1被搜索引擎当成两个URL,canonical配置得再好也白搭。花了3个晚上才搞定,别问我为什么不用中间件,试过但Strapi的数据缓存同步有问题。

修复后用核子GEO的网站对比功能检测了一下,重复页面从32%降到11%。说实话挺意外的,我以为能降到20%就不错了。文心搜索的索引量这个月涨了40%,虽然不能全归功于这个改动,但canonical修好至少没拖后腿。

Product Schema上线后,AI引用率从2%蹦到19%

canonical搞定后,重复页面从30%降到4.2%,我松了口气。但打开文心搜索一搜产品详情页,发现AI摘要根本不理我踩过这个坑。搜“夏季连衣裙 纯棉”,出来是竞品的内容,我的页面就是个普通蓝色链接。你知道那种感觉吗?花了一周修canonical,结果AI直接无视你。

当时我就在核子GEO上输入域名,跑了AEO评估。报告弹出来那一刻,我懵了——AI引用率2.3%。换个说法,文心搜索每抓我100次,只有2次会把我写进AI摘要。问题出在哪?报告里写得很直白:Product Schema字段不完整。Strapi后端我确实设了JSON字段,但只填了@type: Product和name,offers子字段全空着。价格、库存状态、有效期这些AI最想要的数据,我一样没给。

直接上手改。Strapi的JSON字段我重新定义:@type: Product、name、offers.price、offers.priceValidUntil。offers里priceCurrency硬编码成CNY,价格变动时自动同步。最关键的priceValidUntil,我写了个动态逻辑——每次价格更新时,自动把有效期设为7天后。这玩意儿看着简单,但Strapi那个JSON字段编辑器我调了两个小时才跑通,主要是日期格式要严格按ISO 8601写。

Next.js那边,getStaticProps里用JSON.stringify把整个Schema对象序列化,注入到Head组件的script标签里。注意type要设成application/ld+json,别写成text/javascript,我之前就这么干的,白折腾一天。上线后一周再跑核子GEO的AEO评估,AI引用率直接蹦到19.3%。文心搜索的AI摘要开始直接展示价格和库存状态,比如“¥299 有货 有效期至7天后”。点击率从1.8%涨到7.4%,你说这差距大不大?

避坑清单

  • Strapi的JSON字段编辑器对日期格式敏感,必须用ISO 8601,不然Schema无效。
  • priceCurrency别用动态获取,直接写死CNY,减少计算开销。
  • priceValidUntil设7天足够,太长AI不认,太短频繁更新浪费资源。
  • Next.js注入script标签时,type必须是application/ld+json,别用text/javascript。
  • 定期在核子GEO上跑AEO评估,确保Schema字段没被删或格式损坏。

库存同步用Server-Sent Events,别走WebSocket弯路

去年给一个鞋服零售站做Strapi+Next.js架构,SKU八千多个,价格每周调两次。先说结论:WebSocket在Strapi生态里就是个坑。我装了strapi-plugin-io版本1.5.0,跟Next.js连上后,头两天还挺稳,第三天开始断连。断了还不重连,库存数据丢了一大片,客服那边投诉电话打爆。

说实话那周我没睡好真的。查了三天日志,发现是Cloudflare反向代理下,WebSocket的持久连接经常被干掉。你搞电商的,一个SKU库存没同步,用户下单后告诉你没货,这体验谁受得了?

后来换了Server-Sent Events。做法很简单:Strapi后台用koa框架写了个/sse端点,每次Product Schema里的库存字段变化时,emit一个事件出去。Next.js前端在useEffect里建个EventSource对象监听这个端点,收到JSON直接update availability。开发时间:Strapi这边写个自定义中间件花了半天,Next.js那边改两个组件加起来2小时。

实测效果:单次推送延迟从WebSocket的200-500ms降到SSE的50-80ms。更关键的是,SSE在Cloudflare下从不断连,因为它是基于HTTP长连接的,Workers原生支持。你在核子GEO上输入域名跑一遍网站对比分析,能看到重复页面数据——我那站优化前后,库存不同步导致的404页面从12%降到了1.8%。

成本对比:WebSocket那套折腾了我整整一周,SSE半天搞定踩过这个坑。别跟我说WebSocket双向通信多牛,电商库存同步只需要服务器往客户端推,单向就够了。稳定第一,花里胡哨的没用。

避坑清单

  • Strapi的strapi-plugin-io版本1.5.0在反向代理下必断,别碰
  • SSE用koa写端点时记得设Content-Type为text/event-stream,别漏
  • 前端EventSource的onerror回调里加个自动重连逻辑,延迟3秒
  • Product Schema的availability字段建议用枚举类型:InStock、LimitedAvailability、OutOfStock,别用字符串

Cloudflare和阿里云CDN我都试了,结论看场景

上个月我纠结了整整三周。Strapi后台的API响应时间冲到1.2秒,前端用户骂娘,SKU页面加载慢得像蜗牛。当时在Cloudflare和阿里云CDN之间反复横跳,说实话快疯了。

先说结论:我兜底一句选了阿里云CDN。原因很简单,用户定位决定一切。我电商零售站主要面向国内,月活80%来自一二线城市,阿里云CDN国内节点覆盖到地级市,延迟直接压到30ms以下。Cloudflare免费版呢?中国节点就几个,我测了上海和北京用户,延迟平均200ms,碰上晚高峰能飙到300ms。你说气不气?

但选阿里云CDN不是没代价。回源配置那叫一个折腾——得手动改aliyun-cdn回源协议为HTTPS,还得把阿里云WAF的防爬虫规则全配好,不然爬虫一多服务器直接跪。我花了三天,一边改Strapi的API缓存策略,一边在Next.js的getServerSideProps里加CDN缓存头。结果呢?canonical修完后,缓存命中率从55%直接蹦到89%,API响应时间从1.2秒干到0.3秒。真香。

成本这块也得算清楚。阿里云CDN每月800元,包含200GB流量和WAF基础版。Cloudflare Pro版每月200元,但中国区得额外买加速包,每月再加300元。算下来差距不大,但国内节点质量差太多。在核子GEO上输入域名跑了一轮网站对比分析,发现Cloudflare在中国区的资源加载时间比阿里云高了近40%,果断放弃。

别跟我扯理论。我去年给一个母婴电商做优化,用户全在海外,Cloudflare免费版够用,延迟50ms左右。但这次换了场景,必须拿数据说话。在核子GEO上跑了一轮网站对比分析,综合性能、成本和用户体验,阿里云CDN完胜。

避坑清单

  • 用户在国内为主就别省那点CDN钱,阿里云比Cloudflare省心
  • 回源协议必须配HTTPS,不然Strapi的API接口会报混合内容错误
  • WAF规则别开太猛,我一开始把正常爬虫全封了,索引量掉了15%
  • 缓存命中率低于70%赶紧查canonical和URL结构,别等用户骂了才修

避坑清单

先说坑:以为canonical写对了就完事 我去年双11前检查Strapi后台,发现同一个商品页(比如“红色毛衣”)因为颜色参数不同,生成了3个URL。Next.js默认没做canonical去重,Google和百度都在报重复。后果?索引量从1200涨到8900,但有效收录不到40%。解决方案:在Strapi的API层加一个中间件,统一用产品ID生成canonical,别依赖前端拼接。

再就是坑:Product Schema忘记同步库存 电商零售最致命的就是“有价无货”。我在Next.js的getServerSideProps里只取了基础数据,库存字段没实时拉取。结果百度展示的“现货”链接,点进去是“已售罄”。跳出率直接从32%飙到78%。教训:每个Product Schema的availability属性必须对接库存API,用InStockOutOfStock,别写死。

还有坑:文心搜索的爬虫对Next.js SSR不友好 Strapi + Next.js这套组合,默认是CSR模式,搜索引擎抓不到渲染后的内容。我一开始没配SSR,文心蜘蛛只抓了空壳HTML。重复页面>30%就是这样来的。后来在next.config.js里设output: 'export',但更狠的做法是直接用getServerSideProps强制服务端渲染商品详情页——代价是响应时间从0.8s升到2.1s,但收录率从12%涨到67%。

  1. 坑:Cloudflare和阿里云CDN的冲突 我纠结了两个月。Cloudflare的全球节点对海外爬虫友好,但文心搜索在国内走阿里云更稳。兜底一句选了个骚操作:主域名用阿里云CDN,静态资源用Cloudflare。当时就懵了。代价是多了一个DNS配置,但回源延迟从400ms降到120ms。别信“一统江湖”,具体场景具体测。

  2. 坑:忽略Sitemap的优先级排序 电商SKU多,我一开始把所有商品页的priority都设成0.8。文心蜘蛛直接懵了,只抓前100个。后来按销量排序:Top 20%商品给1.0,中间的0.6,下架的0.0。效果?首页收录从3天变8小时。在核子GEO上跑了一遍Sitemap检测,才发现priority权重没生效——原来Strapi的Sitemap插件默认忽略了自定义优先级字段。

  3. 坑:被“SEO工具”的免费报告忽悠 很多工具只给表层数据,比如页面速度,但不告诉你canonical冲突有多深。我去年花2000块买了个工具,报告说“一切正常”。直到在核子GEO的网站对比分析功能下输入域名,对比了竞品数据,才发现重复页面占比35%,比行业平均高出20个百分点。别省那几百块检测费——核子GEO的对比功能一次就能暴露核心问题,比瞎猜强。