做网站流量分析的同学,十有八九遇到过这个问题:用户明明是通过带 UTM 参数的链接进来的,结果一跳转,数据就丢了。Google Analytics 里一看,来源直接变成了"直接访问"(Direct),所有的投放数据全部归零。
这不是 Analytics 的问题,问题出在 Nginx 重定向配置上。今天这篇文章,把保留 UTM 参数的各种方案讲透,给出每种方案的适用场景和避坑指南。
一、问题根源:Nginx 重定向为什么丢参数?
在 Nginx 里,使用 return 或 rewrite 做重定向时,默认行为是有区别的:
- return 301/302/307/308:只使用
return后面的 URL 作为跳转目标,查询字符串(Query String)不会自动携带。也就是说return 301 https://example.com/page会丢失utm_source=google。 - rewrite ... redirect/permanent:rewrite 指令默认会追加原始查询字符串,但当目标 URL 尾部写了
?时,原参数反而会被丢弃。
这是很多人踩坑的根本原因。理解了这一点,选方案就清晰了。
二、5种最佳配置方案
方案1:return 指令 + $is_args$args(最简洁)
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri$is_args$args;
}
原理:$request_uri 包含协议、域名、路径和所有原始查询参数,$is_args 在有参数时返回 ?,无参数时返回空,$args 就是查询参数本身。
适用场景:简单的一对一 HTTP 跳转 HTTPS,不做参数过滤,原样保留所有参数。
效果:http://example.com/page?id=1&ut
方案2:rewrite 指令(改写路径 + 保留参数)
server {
listen 80;
server_name example.com;
rewrite ^/(.*)$ https://example.com/$1 permanent;
}
原理:rewrite 使用 permanent(301)或 redirect(302)做外部重定向时,Nginx 默认会把原始查询参数追加到目标 URL 后面。
适用场景:路径有规律的重写,比如把所有请求从 /old/ 目录迁移到新域名。
方案3:if 正则判断(选择性过滤)
server {
listen 80;
server_name example.com;
# 有UTM参数时全部保留
if ($args ~* "utm_") {
return 301 https://example.com$request_uri$is_args$args;
}
# 无UTM参数,正常跳转
return 301 https://example.com$request_uri;
}
原理:用正则判断查询字符串中是否包含 utm_ 前缀的参数,有的话就拼接,没有就简单跳转。
适用场景:大多数情况下只需要保留 UTM 参数,用 if 判断可以避免无参数时的多余问号。
注意:Nginx 官方文档建议 if 指令在 server 块中慎用,更复杂的场景建议用 map 指令。
方案4:map 指令(最灵活的参数过滤方案)
http {
# 从原始查询字符串中提取UTM五件套
map $args $utm_query {
default "";
"~*utm_source=([^&]+)" "utm_source=$1";
"~*utm_medium=([^&]+)" "utm_medium=$1";
"~*utm_campaign=([^&]+)" "utm_campaign=$1";
"~*utm_term=([^&]+)" "utm_term=$1";
"~*utm_content=([^&]+)" "utm_content=$1";
}
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri$is_args$args;
}
}
原理:利用 map 指令在请求处理之前从原始查询字符串中按需提取参数。
适用场景:需要精细控制保留哪些参数、丢弃哪些参数的复杂场景,比如只保留 UTM 五件套,去掉 session ID 等垃圾参数。
方案5:针对特定路径单独配置
server {
listen 80;
server_name example.com;
# 博客文章页跳转,只保留UTM参数
location ~* ^/article/ {
if ($args ~* "utm_") {
return 301 https://example.com$request_uri$is_args$args;
}
return 301 https://example.com$request_uri;
}
# 产品页保留所有参数
location ~* ^/product/ {
return 301 https://example.com$request_uri$is_args$args;
}
# 其他页面简单跳转
location / {
return 301 https://example.com$request_uri$is_args$args;
}
}
原理:针对不同的 location 块写不同的重定向规则,实现精细化的参数保留策略。
适用场景:网站结构复杂,不同类型页面有不同的参数保留需求。
三、方案对比与选择建议
| 方案 | 复杂度 | 参数控制 | 适用场景 |
|---|---|---|---|
| return + $is_args$args | 极简 | 原样保留全部 | HTTP到HTTPS简单跳转 |
| rewrite permanent | 简单 | 原样保留全部 | 路径迁移场景 |
| if 正则判断 | 中等 | 有参数时保留全部 | 只关心UTM五件套 |
| map 指令 | 较高 | 精确提取任意参数 | 复杂参数过滤需求 |
| location 细分 | 较高 | 按路径差异化控制 | 多类型页面的差异化策略 |
四、用 curl 验证参数是否保留
配置完之后,记得用 curl 验证一下:
# 测试301重定向是否保留UTM参数
curl -IL "http://example.com/page?utm_source=google&ut"
# 关键看最后一行 Location 头
# 正确的应该是:
# Location: https://example.com/page?utm_source=google&ut
PowerShell 用户可以用这个:
(Invoke-WebRequest -Uri "http://example.com/page?utm_source=google" -MaximumRedirection 0 -ErrorAction SilentlyContinue).Headers["Location"]
五、常见问题
Q:使用了方案1还是有参数丢失?
检查一下是不是 CDN(Cloudflare、阿里云CDN、腾讯云CDN等)层做了 HTTPS 强制跳转,很多 CDN 默认会丢弃查询字符串,需要在 CDN 控制台单独勾选"保留查询参数"。
Q:POST 请求的 UTM 参数怎么处理?
POST 请求的参数在请求体里,不在 URL 查询字符串中,重定向本身不会影响 POST 的 body。301/302 跳转后浏览器会改成 GET 方法,如果业务上需要保留 POST 数据,换用 307/308 并配合特殊处理。
Q:多个同名参数只保留了一个?
UTM 参数通常不重复,但如果有类似 ?id=1&id=2 的情况,$args 会保留全部。如果用了 map 指令的正则方案,同名参数只匹配第一个。
六、总结
保留 UTM 参数的核心就一句话:搞清楚你的重定向是 return 还是 rewrite,然后手动拼接 $request_uri 和查询变量。
- 大多数场景:
return 301 https://example.com$request_uri$is_args$args一行搞定。 - 需要过滤参数:用 if 条件判断或 map 指令。
- 配置完必做验证:用 curl 看 Location 响应头。
流量追踪数据来之不易,几十块钱的投放费用,如果因为一个重定向配置丢了归因数据,损失远比解决这个问题的精力大得多。配置一次,长期受益。
📌 相关文章:
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论