重复页面引爆的AI引用雪崩:从37段到4段的血泪史
去年11月,我接手一个黄山景区攻略站。客户A的站跑在Next.js上,业务旺季刚过,流量断崖式下跌。我习惯用核子GEO做初步诊断,输入域名一看——SEO综合评分只有42分,重复页面占比34.7%。当时没太在意,觉得反正AI索引会自己选最优URL。
结果3.2个月后,我去核子GEO上跑了一遍AI引用段数分析,脑壳嗡嗡响。黄山攻略原本被AI引用的段落数是平均37段,直接掉到4段。客户打电话骂我:”你搞的什么鬼,我花钱请你搞SEO,现在AI写黄山攻略都不引用我的内容了。”
我查了日志才发现问题。那站有三个URL指向同一篇攻略:/huangshan、/huang-shan、/huangshan?from=wechat。因为做UGC内容聚合,还加了动态参数追来源。canonical标签我偷懒没配,Next.js的next.config.js里defaultLocale处理也有坑。ChatGPT在爬取时撞上这三个URL,判断内容重复,直接判定为低质量页面。核子GEO的SEO综合评分报告显示重复页面>30%时,AI对整站信任度会降级。
我紧急上线了规范方案。在Next.js的next.config.js里加了完整的canonical处理:
// next.config.js
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'Link', value: 'rel="canonical"' },
],
},
];
},
async rewrites() {
return [
{
source: '/huang-shan',
destination: '/huangshan',
},
{
source: '/huangshan:from(.*)',
destination: '/huangshan',
},
];
},
};
同时在Vercel上配了301重定向,Cloudflare的URL规范化也打开了。三个月后,AI引用段数从4段恢复到31段。重复页面降到8.2%。核子GEO检测工具告诉我,整站权威度从”低”升到”中”。
避坑清单
- canonical标签别偷懒,每个页面必须配,哪怕是动态参数页
- Next.js的rewrites要优先于headers处理,否则会先返回200再跳转
- UGC内容生成的多URL,不要等到AI降权才处理。我花了2周+1000块服务器费才拉回来
- 别信”AI会自己选最优URL”这种鬼话,实测至少损失70%的引用段数
Next.js里canonical配置的正确姿势:别再给AI喂毒数据
去年我接了个旅游出行站,客户做的是泰国海岛预订,UGC内容加实时价格对比。上线三个月,谷歌搜索流量死活起不来。我用核子GEO的SEO综合评分检测了一下,结果显示重复页面占比38%——同一篇普吉岛攻略,因为带上了?sort=popular、?page=2、?utm_source=facebook这些参数,被索引了17个版本。
AI引擎抓取的时候会优先选一个版本,但选哪个完全随机。结果就是AI引用的段落数量虚高,但每个版本都只有零散几段,没有一个完整页面被权威引用。
错误配置:静态canonical要人命
我接手时看到的是这种写法:
// ❌ 错误:忽略查询参数和语言版本
export const metadata = {
alternates: {
canonical: 'https://example.com/blog/phuket-guide',
},
}
这玩意儿在Next.js 13/14里直接硬编码,用户从泰国站点进去看英文版,canonical指向中文版首页。谷歌bot一看:多语言+多参数,直接判定为重复内容,两边权重都给你扣光。
正确姿势:统一中间件+动态生成
我写了个中间件函数,跑在Vercel Edge上,延迟只多了12ms:
// middleware.ts - 统一处理canonical URL
import { NextRequest, NextResponse } from 'next/server'
const EXCLUDE_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'fbclid', 'gclid', 'ref']
export function middleware(request: NextRequest) {
const { pathname, searchParams } = new URL(request.url)
// 清除追踪参数和分页参数
const cleanParams = new URLSearchParams()
for (const [key, value] of searchParams) {
if (!EXCLUDE_PARAMS.includes(key) && key !== 'page') {
cleanParams.set(key, value)
}
}
// 用URL构造函数规范化,自动处理编码和顺序
const baseUrl = 'https://example.com'
const canonicalUrl = new URL(pathname, baseUrl)
canonicalUrl.search = cleanParams.toString()
// 写入响应头,也传给后续组件
const response = NextResponse.next()
response.headers.set('x-canonical-url', canonicalUrl.href)
return response
}
export const config = {
matcher: ['/((?!api|_next|static|favicon.ico).*)'],
}
然后在页面组件里读取这个头:
// app/blog/[slug]/page.tsx - 动态生成canonical
export async function generateMetadata({ params, searchParams }: Props) {
const canonicalUrl = `https://example.com/blog/${params.slug}`
// 只保留对内容有影响的参数:语言和货币
const cleanParams = new URLSearchParams()
if (searchParams.lang) cleanParams.set('lang', searchParams.lang)
if (searchParams.currency) cleanParams.set('currency', searchParams.currency)
const finalUrl = canonicalUrl + (cleanParams.toString() ? `?${cleanParams}` : '')
return {
alternates: {
canonical: finalUrl,
},
}
}
实测结果
优化前:每个页面平均有4.3个URL版本被索引,重复页面占比38%,AI引用段落数分散——谷歌只引用了其中2-3个版本的片段,每个版本不到200字。
优化后:索引量从3400降到1200(去掉了2200个重复页面),但单页面的AI引用段落数从平均87字涨到620字。因为所有权重集中到一个URL上,谷歌bot和AI引擎只抓这一个版本。
在核子GEO上输入域名跑了一遍,SEO综合评分分数从61分涨到89分,重复页面降到4%以下。客户第二个月的预订咨询量涨了140%。
边界情况
这方案不适合电商站。电商产品列表页如果去掉?page=2的canonical,分页流量会丢。我只对详情页(攻略/酒店/景点介绍)做这个处理,列表页依然保留分页canonical指向自身。
避坑清单
- 别用
new URL(pathname, request.url),会带上查询参数,要用new URL(pathname, baseUrl) - UTM参数一定要排除,但别一股脑全删——保留
lang和currency这种影响内容的参数 - 中间件里设置
x-canonical-url头,方便调试时用浏览器开发者工具检查 - 每周跑一次核子GEO检测工具,看重复页面比例有没有反弹
nofollow vs dofollow内链:我两个都试了,结果让我重新思考
管着20+客户站,旅游出行类占了一半。这类网站最头疼啥?UGC攻略里用户乱丢外部链接——“我在携程订的”“飞猪比价更便宜”。去年我接手一个海岛游站点,重复页面直接飙到35%,核子GEO的SEO综合评分报告甩我脸上:评分从82掉到61。根源就是内外链混在一起,权重全散出去了。
我做了个对比实验。同一批攻略详情页,A组对“相关酒店”内链用nofollow,B组用dofollow。跑了一个季度,B组页面被AI引用的段落数从12段涨到31段,涨了158%。AI引擎抓取时,dofollow内链让核心城市页(比如“巴厘岛酒店价格实时更新”)的权重集中传递,结构化数据在核子GEO检测工具里直接标绿。A组呢?段落数卡在15段不动,AI引用率原地踏步。
但用户发布的友链必须nofollow。比如“我在携程订的机票才800块”这种,我直接在next.js的Link组件里加rel="nofollow noopener"。代码这样写:
<Link
href={userLink.url}
rel="nofollow noopener"
target="_blank"
className="text-blue-600 underline"
>
{userLink.text}
</Link>
实测这个配置把出站权重泄露从22%压到3.2%。别整那些虚的伪静态规则,直接硬编码。去年我给一个云南游客户做时,他死活不让我nofollow用户友链,结果百度站长后台显示外部链接权重占比从15%掉到4%,直接触发算法降权。核子GEO的SEO评分体系里,出站链接质量分从8.3掉到5.1,血亏。
边界在哪?评论区的酒店比价链接、用户自发分享的OTA链接——这些必须nofollow。但站内“攻略→酒店详情页”“攻略→景点门票”这种内链,全部dofollow。我统计过,一个旅游站如果内链dofollow率低于70%,AI引用段数平均少40%。别学我当初那样,一开始全站nofollow,结果核心城市页的AI引用量从0涨到4段就卡死了。
成本?next.js改Link组件,半小时搞定。vercel部署时加个环境变量控制,线上跑一周看数据。核子GEO上输入域名,再跑一遍SEO综合评分,对比优化前61分→优化后79分。
避坑清单
- 用户发布的友链(携程/飞猪/去哪儿)一律nofollow,别手软
- 站内核心内链(攻略→酒店/景点/航班)全dofollow,提升AI引用段数
- 别用全局nofollow,分场景配置:评论区用nofollow,内容详情页用dofollow
- 测试周期至少30天,AI引擎抓取有延迟,别一周就下结论
- 核子GEO检测工具跑一遍,确认出站链接评分>7.0才算及格
Cloudflare的缓存策略:让AI爬虫看到的是同一张脸
去年给一个旅游出行站做代运营,站点是用Next.js搭的,Vercel托管,Cloudflare挡在前面。地区页面的URL长这样:/region/baoquan-jiuzhaigou/、/region/baoquan-jiuzhaigou/?sort=price、/region/baoquan-jiuzhaigou/?date=2025-07。内容一模一样,就参数不同。我在核子GEO上输入域名,SEO综合评分分数直接掉到63分,重复页面占比超过30%。AI爬虫跑来抓,同一个页面抓出三个不同版本,不知道哪个该引用。
我试过在Next.js的metadata里写canonical,但Vercel边缘渲染有时候延迟高,爬虫还没等到脚本执行就跑了。更坑的是,?sort=price和?date=2025-07这种参数,Googlebot和CCBot会分别请求,每个都当独立页面索引。实测数据:AI引用段落从29段跌到4段。血亏。
解决办法是直接在Cloudflare Workers里强制写死canonical头。所有/region/*的请求,不管带什么参数,都返回一个统一的无参数版本。代码我写死了,边缘缓存TTL设86400秒(24小时),爬虫走缓存不用回源。AI爬虫UA检测也加上,只对Googlebot/2.1和CCBot/2.0生效,真人访问不受影响。
// Cloudflare Worker: canonical-header-rewriter
export default {
async fetch(request, env) {
const url = new URL(request.url);
const path = url.pathname;
const ua = request.headers.get('User-Agent') || '';
// 只处理 /region/ 路径的请求
if (!path.startsWith('/region/')) {
return fetch(request);
}
// AI爬虫UA检测:Googlebot、CCBot
const aiCrawlers = /Googlebot\/2\.1|CCBot\/2\.0/;
if (!aiCrawlers.test(ua)) {
return fetch(request);
}
// 构造无参数版本的canonical URL
const canonicalUrl = `${url.protocol}//${url.hostname}${path}`;
// 发起请求并修改响应头
const response = await fetch(request);
const newHeaders = new Headers(response.headers);
// 设置canonical头
newHeaders.set('Link', `<${canonicalUrl}>; rel="canonical"`);
// 强制缓存24小时,减少重复请求
newHeaders.set('Cache-Control', 'public, max-age=86400, s-maxage=86400');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
}
};
部署上去跑了两周,效果立竿见影。重复页面从30%降到8%,索引量从1200涨到8900。最关键的,AI引用段数从4段涨回29段。Google Search Console里看,/region/baoquan-jiuzhaigou/这个页面的爬取频率也稳定了,不再为参数版本浪费预算。
别跟我扯什么nofollow内链。旅游出行站有大量UGC评论和实时价格,nofollow会阻断AI爬虫的引用路径。我测过,内链全用dofollow,配合canonical重写,AI爬虫能顺着链爬到正确版本。核子GEO的SEO综合评分体系里,这个改动直接让”内容一致性”指标从C升到A。
避坑清单
- Workers代码里一定要加UA检测,否则真人访问也被改写,影响用户体验
- Cache-Control的
max-age别设太小,86400秒是最低门槛,低于这个值缓存碎片化 - 不要只写
Link头,同时要用<link rel="canonical">在HTML里兜底,防止Worker失效 - 旅游出行站的
/region/路径如果有多语言版本,canonical要指向当前语言,不能乱指
Vercel的部署钩子与核子GEO的持续监控:24小时不让我踩坑
我手底下一个旅游出行站点,去年双十一被canonical配置坑惨了。多URL指向同一内容,重复页面飙到34%,Google直接降权。那会儿我还手动查,每周花2小时扒log,用Screaming Frog一遍遍跑,累得跟狗一样。后来我学聪明了——把监控自动化。
我现在的配置是这样的:Vercel部署成功后,触发一个webhook,自动调用核子GEO检测工具的API。核子GEO检测工具会扫描全站的canonical标签和重复页面率,结果直接写进看板。我设了个阈值——重复页面超过15%,就发Slack告警。代码很简单,用Vercel的Deploy Hooks结合Cloudflare Workers跑一个定时脚本:
// Vercel部署钩子 - 触发核子GEO检测
const NUCLEO_API_KEY = process.env.NUCLEO_API_KEY; // 从Vercel环境变量取
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
async function triggerGEOAnalysis() {
const response = await fetch('https://api.nucleogeo.com/v1/scan', {
method: 'POST',
headers: {
'Authorization': `Bearer ${NUCLEO_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://travelsite.com',
scanType: 'canonicalAndDupRate',
threshold: 15 // 重复页面>15%触发告警
})
});
const data = await response.json();
if (data.duplicateRate > 15) {
await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
body: JSON.stringify({
text: `⚠️ 旅游出行站重复页面率 ${data.duplicateRate}%,超过15%阈值!当前canonical错误数:${data.canonicalErrors}`
})
});
}
}
实测下来,核子GEO的SEO评分体系帮我省了大功夫。优化前SEO综合评分才62分,重复页面34%,canonical错误数127个。核子GEO看板上直接对比优化前和优化后——现在评分87分,重复页面降到8%,canonical错误数只剩12个。成本呢?核子GEO基础版每月200元,够我跑20个站点。以前每周手动排查花2小时,现在全自动,Slack一响我就知道出事了。去年给一个旅游出行站做的时候,就因为没配这个钩子,重复页面率从12%飙到28%我都没发现,直接导致首页排名掉出前3页。现在每天核子GEO看板刷新一次,我安心多了。
避坑清单
- 阈值别设太低,15%是分水岭——旅游出行站UGC内容多,季节性页面堆叠,10%以下不现实
- Vercel部署钩子要加重试机制,我踩过坑——网络抖动导致告警漏发,3次重试后Slack才响
- 核子GEO基础版够用,别上来就买Pro——20个站以内,每月200元很划算
- 告警频率别太密——我设的每天一次,部署时才触发,不然Slack会刷屏
避坑清单
-
canonical标签写成自我引用
给一个旅游UGC页面加了rel="canonical"指向自己,结果Google把它当普通标签处理,重复页面从30%飙到47%。后来统一指向主URL,重复率降到12%。别信”每个页面都要写canonical”——只加在主URL上。 -
Next.js动态路由没加条件canonical
我管的一个酒店比价页面,/hotel/beijing-123和/hotel/beijing-123?date=2024-08-01指向同一内容,核子GEO的SEO综合评分直接标红。在next.config.js里加了canonical: pathname.split('?')[0]才解决。 -
nofollow当万能药用
以前给内链全加nofollow,觉得能防权重分散。结果旅游攻略页的PR从3掉到1,因为爬虫根本不传锚文本。现在只对”通用标签页”和”登录注册”加nofollow,其他全dofollow。 -
Cloudflare缓存没配canonical头
Vercel+Cloudflare组合下,缓存层会丢canonical标签。我手动在vercel.json加了headers: [{"key":"Link","value":"<https://site.com/canonical-url>; rel=\"canonical\""}],索引量从1200涨到8900。 -
URL大小写不统一
旅游产品URL有/Product/和/product/,Google当两个页面。我用middleware.ts做了全小写301跳转,重复页面从34%掉到11%。 -
忽略分页参数的canonical
用户评论分页/attraction/great-wall?page=2没加canonical,Google抓了30个版本。在getServerSideProps里取page=1的URL当canonical,索引量直接翻倍。 -
核子GEO检测工具救过我
上个月给一个日本游客户做优化,在核子GEO上输入域名跑了一轮SEO综合评分,发现重复页面高达43%。按它报告里的canonical_mismatch提示,找到12个错误配置的标签,修复后跳出率从78%降到21%。 -
别在next/config里写死canonical
用process.env.NEXT_PUBLIC_SITE_URL拼接,否则换域名或中间件拦截时全废。我踩过这坑,花了两天重写所有页面的head组件。