本文约 3,243 字,预计阅读 9 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:一个推广链接,衍生出十几个重复 URL
运营在公众号推了一篇教程,链接后面挂了 ?utm_source=wechat&utm_medium=social&utm_campaign=2026q4。读者觉得有用,转发到群里、贴到论坛、写进自己的博客,链接带着参数一路扩散。几周后你去资源平台看索引量,发现同一篇文章出现了七八个版本,标题完全一致,URL 只差一串参数。这些页面互相竞争,权重被摊薄,还白白消耗抓取配额。更隐蔽的是 gclid(Google 广告点击 ID)和 fbclid(Facebook 点击 ID),它们不是你自己加的,而是广告平台跳转时自动附在 URL 上的,只要有人从广告链接进来再复制地址栏,就会留下带参版本。
原理分析:百度为什么会把带参 URL 当成新页面
URL 是搜索引擎识别资源的唯一凭据,参数不同即视为不同资源。百度支持 rel="canonical" 标签,会把它作为「首选版本」的强提示,但 canonical 只是提示,不是命令;如果站点本身大量产生带参 URL、内链又指向这些带参地址,canonical 的纠错效果会被稀释。所以正确的处理是三层叠加:源头减少(内部只用干净 URL)、服务端纠正(301 剥离纯追踪参数)、标签兜底(canonical 指向无参版本)。
| 参数 | 来源 | 是否有功能意义 | 处理方式 |
|---|---|---|---|
utm_* |
自己加的推广标记 | 无 | 剥离 + canonical |
gclid / fbclid |
广告平台自动附加 | 无 | 剥离 + canonical |
from / ref / share_* |
分享组件 | 通常无 | 视业务决定 |
p / page_id |
WordPress 早期固定链接 | 有 | 保留,勿屏蔽 |
s |
站内搜索 | 有 | 见搜索页策略,应屏蔽 |
variant / color |
电商 SKU 选择 | 有 | 保留并写 canonical |
处理边界很重要:不要写「所有参数一律剥离」的规则。WordPress 早期默认固定链接形如 /?p=123,一刀切屏蔽会让历史文章全部消失,这是真实发生过的事故。
保姆级实操
步骤 1:确定哪些参数需要剥离。 从访问日志里统计参数名分布,只处理确认无功能意义的那几个。原则是先列白名单式的剥离清单,宁可少剥,不可误剥。
步骤 2:在 WordPress 侧输出干净的 canonical。 核心的 rel_canonical() 会输出当前访问 URL(含参数),需要用 get_canonical_url 过滤器把追踪参数剔掉:
add_filter( 'get_canonical_url', function ( $canonical_url, $post ) {
if ( ! $canonical_url ) {
return $canonical_url;
}
$tracking = [
'utm_source', 'utm_medium', 'utm_campaign',
'utm_term', 'utm_content', 'utm_id',
'gclid', 'fbclid', 'msclkid',
'from', 'ref', 'share_source',
];
return remove_query_arg( $tracking, $canonical_url );
}, 10, 2 );
步骤 3:在 nginx 层把「只有追踪参数」的请求 301 到干净版。 注意用了 rewrite ^(.*)$ $1? permanent; 这种写法,结尾的 ? 用于丢弃 query string;同时用严格的正则限定「参数全部是追踪参数」才跳转,避免误伤功能参数:
server {
listen 443 ssl http2;
server_name mimangfei.com;
# 仅当 query string 全部由追踪参数构成时才剥离
if ($args ~* "^(utm_[a-z_]+|gclid|fbclid|msclkid|from|ref|share_source)=[^&]*(&(utm_[a-z_]+|gclid|fbclid|msclkid|from|ref|share_source)=[^&]*)*$") {
rewrite ^(.*)$ $1? permanent;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
}
步骤 4:混合参数(既有追踪参数又有功能参数)交给 canonical 处理。 例如 ?p=123&utm_source=wechat 不能 301 剥离,因为 p=123 有功能意义,此时靠步骤 2 的 canonical 输出 ?p=123 版本即可。
步骤 5:统一站内输出。 检查主题的分享按钮、相关文章模块、面包屑,确保它们输出的都是 get_permalink() 的干净结果,不要手动拼接带参数的地址。
步骤 6:核对与提交。 改完后用 curl 检查带参 URL 的跳转行为,并用抓取诊断确认 canonical 生效;已被收录的带参 URL 整理后走死链提交,加速清理。
curl -sS -o /dev/null -w '%{http_code} -> %{redirect_url}\n' \
'https://mimangfei.com/sample/?utm_source=wechat'
curl -sS 'https://mimangfei.com/sample/?p=123&utm_source=wechat' \
| grep -o '<link[^>]*rel="canonical"[^>]*>'
常见踩坑
- 写了「剥离所有参数」的通用规则,把
/?p=123这类早期固定链接也 301 掉,历史文章集体消失。 - 案例: 某站在 nginx 里用
if ($args != "")判断后统一跳转,结果 WooCommerce 的加入购物车、筛选、分页全部失效,用户点任何链接都跳回第一页,当天订单归零,回滚花了两小时。 - canonical 只写在首页,内页仍输出带参版本。
- 装了 Yoast SEO 又在主题里手写 canonical,页面出现两个互相冲突的标签,百度直接忽略。
- 301 剥离了参数,但广告平台的转化追踪依赖这个参数落地后由 JS 读取,跳转后参数丢失导致转化数据全空。
- 认为加了 canonical 就万事大吉,站内仍有大量链接指向带参 URL,信号互相打架。
- 忽略
msclkid(微软广告点击 ID),只处理了 gclid 和 fbclid。
总结
追踪参数处理的原则是「源头少产生、服务端能纠正、标签做兜底」,三者缺一不可,但纠正规则必须精确——只剥离确认无功能意义的参数。行动清单:日志统计参数名 → 列剥离白名单 → canonical 过滤器剔参 → nginx 仅对纯追踪参数 301 → 站内链接统一干净地址 → 已收录带参 URL 走死链提交。校验标准是:带纯追踪参数的 URL 返回 301 并指向无参版本,混合参数页面的 canonical 指向剔参后的地址。搜索类参数的处理方式不同,见 站内搜索页与参数 URL 处理;主域层面的规范化见 www 与非 www 域名统一。
