前言:UTM参数消失的罪魁祸首找到了
你在Google Analytics里看到一堆direct流量,百思不得其解:链接明明带了utm_source、utm_medium、utm_campaign,用户点进来的时候也有,怎么就变成direct了?原因很可能就藏在你的重定向链里。
本文从真实故障场景出发,解释清楚:为什么多次重定向会把UTM参数一层层剥掉、怎么快速诊断、以及从哪一层开始根治。
一、先搞清楚:为什么重定向会丢掉参数
1.1 每个跳转都是独立的HTTP请求
重定向不是页面搬运。HTTP重定向的实质是:浏览器先请求A地址,服务器返回302或301,浏览器再发起一个全新的请求到B地址。这个新请求默认不带上一个地址的查询参数。
当你的架构涉及CDN层→Nginx层→后端应用层时,每一层都可能各自做一次重定向。UTM参数就这样在第三个、第四个请求里消失得无影无踪。
1.2 三类最常见的参数剥离点
- CDN强制HTTPS跳转:阿里云CDN、腾讯云CDN、Cloudflare的始终使用HTTPS功能,默认只做跳转,不保留查询字符串。
- Nginx rewrite或return不带参数:用return 301跳转时,如果直接写目标URL不带is_args和args变量,参数必然丢失。
- 后端框架默认行为:Java Spring、Python Django、PHP Laravel等框架做HTTPS重定向时,默认清除查询参数。
1.3 curl追踪重定向链路
在本地用curl可以完整还原整个跳转链:
curl -vL "https://www.example.com/?utm_source=baidu"
加上-vL后,curl会跟随所有重定向。-v输出的Location行就是每一跳的目标地址。如果看到目标地址里没有utm_xxx,说明参数在那一跳被丢弃了。
二、5个诊断步骤:快速定位UTM参数在哪一跳丢失
步骤1:用curl -vL看完整链路
对着带UTM参数的URL执行,观察每一行Location:
curl -vL "https://你的域名/?utm_source=baidu&utm_medium=cpc&utm_campaign=test" 2>&1
步骤2:检查Chrome开发者工具Network面板
在Network面板勾选Preserve log,访问带UTM的链接,过滤301/302响应。看每一行的Request URL列,参数在哪一行消失了,哪个跳转就是问题节点。
步骤3:逐层禁用跳转,确认故障层
如果架构是CDN→Nginx→后端,依次绕过:直接请求源站IP看源站是否有重定向,在本地hosts绑 hosts绕过CDN再测一遍,用curl直接请求Nginx看CDN层的跳转配置。
步骤4:检查Nginx日志的dollar sign+request_uri
在access_log里打出完整的原始请求URI(包含参数):
log_format utm_log dollar sign+request_uri;
access_log /var/log/nginx/utm.log utm_log;
日志里没有utm_xxx,说明参数在到达Nginx之前就已经丢了。
步骤5:检查CDN控制台配置
登录CDN控制台,查看强制HTTPS跳转、HTTP→HTTPS、页面规则这几项。如果配置了回源跟随且回源协议是HTTP,参数可能在CDN回源过程中丢失。
三、3层根治方案:让UTM参数穿过多跳
方案1:Nginx层——正确写法保留所有参数
Nginx的return指令做301跳转时,默认不带参数。正确写法:
# 写法一:最简洁,推荐
return 301 dollar sign+scheme://dollar sign+host+dollar sign+request_uri;
# 写法二:强制HTTPS保留参数
return 301 https://dollar sign+host+dollar sign+request_uri;
# 写法三:判断条件后跳转
if (dollar sign+scheme = http) {
return 301 https://dollar sign+host+dollar sign+request_uri;
}
核心变量是dollar sign+request_uri,它包含原始URI+原始查询字符串。注意不要在目标URL后额外加问号,否则会触发双重问号问题,参数同样丢失。
方案2:CDN层——开启查询参数透传
Cloudflare Page Rule示例(保留所有参数):
匹配模式: *example.com/*
转发到: https://www.example.com/dollar sign+2?utm_no_cache=1
状态码: 301
或者用Transform Rules:保留原始查询字符串。
阿里云CDN/腾讯云CDN:在回源配置里找是否携带源站查询参数,确保开启。
方案3:后端应用层——Java/Python/Node.js统一处理
在代码里做重定向时,统一使用框架的标准方法:
# Java Spring Boot
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
.location(URI.create("https://newhost" + request.requestURI + "?" + request.queryString))
.build()
# Python Flask
from urllib.parse import urlencode
return redirect(f"https://newhost{request.path}?{request.query_string}", code=301)
# Node.js Express
res.redirect(301, https://newhost);
关键原则:使用原始请求对象而不是硬编码的路径。
四、完整排查流程
- 用curl -vL访问带UTM参数的URL,查看每一跳Location
- 定位参数在哪一跳消失
- 如果消失发生在CDN层:检查CDN控制台的HTTPS跳转规则
- 如果消失发生在Nginx层:检查return/rewrite配置是否用了dollar sign+request_uri
- 如果消失发生在后端:检查代码里的重定向方法是否接住了queryString
- 每一层修复后,重新用curl验证
五、最容易踩的3个坑
- 双重问号:目标URL末尾加了?,然后args又追加了一次,变成?utm_source=baidu?utm_source=baidu,参数直接失效。
- CDN缓存了跳转规则:修改CDN配置后要清除缓存,否则旧规则还在生效。
- 302和301行为不同:某些CDN的302会把参数丢掉,优先用301。
结语
多次重定向剥UTM参数的本质,是每一跳的跳转配置者没有主动把参数接住往下传。在架构简单的情况下,用好Nginx的dollar sign+request_uri变量就能解决大部分问题。架构越复杂(CDN多层、后端多服务),越要在每一层的跳转规则里明确声明参数保留策略。
把这套排查方法存下来,下次GA4里又出现一堆direct流量的时候,就知道从哪里开始查了。
相关文章
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论