用 Nginx 的 return 指令做 301 重定向时,很多人会遇到查询参数丢失、URL 出现双重问号、或者参数莫名其妙被覆盖的问题。本文从原理出发,配合真实案例,讲清楚 return 301 后面怎么拼接问号和参数,以及哪些写法是错的。
一、return 301 默认行为:查询参数会丢失
先记住一个核心事实:Nginx 的 return 301 $url 默认情况下不会自动携带原请求的查询参数。
server {
listen 80;
server_name example.com;
return 301 https://example.com/new-page;
}
访问 https://example.com/old-page?id=5&name=test,浏览器会被跳转到 https://example.com/new-page,后面什么都没有——?id=5&name=test 全部丢失。
这是因为 return 指令里的 URL 如果没有显式写查询字符串,Nginx 就不会附加任何参数。这和 rewrite 的默认行为不一样,rewrite 会在特定情况下自动追加原始参数。
二、三种正确的拼接写法
写法一:使用 $request_uri 自动携带全部参数(最常用)
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
$request_uri 包含完整的原始 URI(包含路径和查询字符串)。这种写法适合做全站 HTTP 到 HTTPS 的强制跳转,原始 URL 的所有信息都能完整传递到新地址。
适用场景:
- 全站 HTTP 跳转到 HTTPS
- 域名更换时的整站重定向
- 需要保留所有原始查询参数的跳转
写法二:使用 $is_args 和 $args 手动拼接(更灵活)
server {
listen 80;
server_name example.com;
return 301 https://example.com/new-page$is_args$args;
}
解释一下这两个变量:
$is_args:如果有查询参数则返回?,没有则返回空字符串$args:原始查询参数字符串(不含问号)
这样写的好处是:跳转目标 URL 是固定的,但同时能选择性保留原始参数。
举例:
- 访问
/test?utm_source=baidu→ 跳转到/new-page?utm_source=baidu - 访问
/test(无参数) → 跳转到/new-page(无问号)
$is_args 的自动判断避免了没有参数时 URL 末尾多出一个孤立问号的问题。
写法三:拼接固定参数 + 保留原始参数
server {
listen 80;
server_name example.com;
return 301 https://example.com/new-page?utm_campaign=redirect$is_args$args;
}
如果你想在跳转时追加自己的营销参数(比如 UTM 追踪码),同时保留原始参数,就这么写。实际 URL 会变成:
- 访问
/page?ref=twitter→ 跳转到/new-page?utm_campaign=redirect&ref=twitter
不过这里有个坑需要注意:原始参数可能和你的固定参数发生键名冲突(比如都有 utm_source),需要用 map 指令做更精细的控制。
三、三种常见错误写法
错误一:写两个问号
# 错误写法
return 301 https://example.com/new-page?$request_uri;
很多人把 $request_uri 当作普通字符串拼进去,结果 URL 变成了:
https://example.com/new-page?/original-path?id=5
路径前面多了一个 /,而且问号出现两次,服务器根本读不对参数。这就是双重问号问题的根源。
如果需要用 $request_uri,直接拼在域名后面,不要再加 ?:
# 正确
return 301 https://example.com$request_uri;
错误二:问号后面直接写 $request_uri
# 错误写法
return 301 https://example.com/new-page?$request_uri;
同错误一,$request_uri 包含完整路径和参数,再加问号就成了双重问号。
错误三:混用 $uri 和 $request_uri
# 有隐患的写法
return 301 https://example.com$uri?page=$arg_page;
这种写法逻辑复杂,不易维护。建议统一使用上述三种推荐写法之一。
四、完整配置示例
示例一:全站 HTTP 跳 HTTPS,保留所有参数
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
server_name example.com www.example.com;
# 正常处理
root /var/www/html;
index index.html;
}
所有 HTTP 请求跳转到 HTTPS,并完整保留原始 URL 和查询参数。
示例二:特定页面跳转到新地址,追加 UTM 参数
location = /old-product {
return 301 https://example.com/new-product?utm_source=website&medium=redirect$is_args$args;
}
适合产品页面迁移场景,既做了跳转追踪,又保留了原始搜索参数。
示例三:多参数场景下安全追加参数
map $args $filtered_args {
default "$args";
}
server {
listen 80;
server_name example.com;
return 301 https://example.com/new-page?utm_source=self$is_args$filtered_args;
}
这个写法先用 map 指令处理参数,再追加新的 UTM 参数,属于进阶用法。
五、用 curl 验证参数是否正确保留
写完配置后,用 curl 加上 -L 跟随重定向和 -I 只看响应头,检查跳转后的 URL 是否正确:
# 检查完整跳转链路和参数
curl -I -L "https://example.com/old-page?id=5&name=test"
# 加上 -w 查看最终 URL(Linux/macOS)
curl -L -w "Final URL: %{url_effective}" "https://example.com/old-page?id=5&name=test"
看输出里 Location: 响应头,确认参数格式是否正确(没有双重问号、没有多余问号)。
在 Linux/macOS 上直接用 curl,Windows PowerShell 里可以用 curl.exe 调用原生 curl,或者参考 PowerShell curl替代方案检查安全响应头。
六、总结
核心要点就三句话:
return 301默认不携带查询参数,必须显式拼接- 用
$request_uri自动带全部参数,或用$is_args$args灵活拼接 - 绝对不要在
$request_uri前再加?,否则必出双重问号
掌握了这些,再配合 curl 验证,Nginx 重定向的参数问题基本不会再困扰你了。
相关推荐
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论