换域名、从 HTTP 切到 HTTPS、把多个老站合并成一个新站,这些操作都会涉及一件事:把旧地址永久告诉浏览器和搜索引擎"这里的东西搬到新地址了"。这件事靠 301 重定向完成。配对了,权重和收录能平滑迁移;配错了,轻则跳转死循环打不开页面,重则 SEO 权重全部丢失。
这篇文章讲清楚 301 是什么、和 302 有什么区别、Nginx 里两种主流写法(return 301 和 rewrite ... permanent)、常见的循环跳转怎么排查,以及配置时容易踩的坑。读完你能独立写出正确的 301 规则,并在出问题时用 curl -I -L 快速定位跳转链。
一、301 是什么,为什么重要
301 表示"永久移动"(Moved Permanently)。服务器返回 301 时,会附带一个 Location 响应头,告诉客户端新地址在哪。浏览器收到后自动跳到新地址;搜索引擎爬虫收到后,会把旧地址的权重和信号转移到新地址,并逐步用新地址替换索引里的旧地址。
这和 302(临时移动,Moved Temporarily)有本质区别:
| 对比项 | 301 永久重定向 | 302 临时重定向 |
|---|---|---|
| 语义 | 资源永久换了地址 | 资源暂时在别处 |
| 浏览器缓存 | 会长期缓存,下次直接跳 | 一般不长期缓存 |
| 搜索引擎 | 传递权重,替换索引 | 保留原地址索引,不传递权重 |
| 典型用途 | 换域名、HTTPS 升级、站点合并 | 临时维护页、A/B 测试、登录跳转 |
| 改回难度 | 用户端有缓存,改回后用户可能仍跳旧地址 | 相对容易 |
关键点:浏览器会对 301 做长期缓存。这意味着一旦你配错了 301,用户浏览器可能"记住"错误跳转很久,即使你改回了配置,老用户照样被带偏。所以 301 一定要在测试环境验证清楚再上生产。
二、Nginx 的两种写法
Nginx 里实现 301 主要有两种方式:return 和 rewrite。
return 最简单直接,性能也最好,适合"整个域名/目录一律跳到某个固定地址"的场景:
# 整个旧域名跳到新域名(保留原路径),注意 $request_uri 带前导斜杠
server {
listen 80;
server_name old.example.com;
return 301 https://new.example.com$request_uri;
}
这里用 $request_uri(原始请求 URI,含查询字符串)比 $uri 更合适,因为 $uri 是规范化后的路径,不带查询参数,容易丢参数。如果你的跳转不需要保留路径,也可以写死目标地址:
server {
listen 80;
server_name old.example.com;
return 301 https://new.example.com/;
}
rewrite 适合"按规则批量改写路径",比如把 /old-path/xxx 映射到 /new-path/xxx:
server {
listen 80;
server_name www.example.com;
# 把 /blog/ 下的文章整体迁移到 /articles/
rewrite ^/blog/(.*)$ /articles/$1 permanent;
}
permanent 就是 301,redirect 是 302,last 和 break 是内部重写不发跳转。注意 rewrite 在 location 块里的行为受规则顺序和 flag 影响,复杂规则建议优先用 return 或拆成独立 location,可读性更好,也不容易出循环。
三、几个典型场景的完整配置
场景一:HTTP 全站跳 HTTPS
这是最常见的需求。核心是让 80 端口的请求全部 301 到 443。
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# 统一跳转到 HTTPS 版本
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# ...业务配置
}
$host 会取请求里的 Host 头,这样 example.com 和 www.example.com 都能正确跳到自己对应的 HTTPS 地址。
场景二:带 www 与不带 www 统一
搜索引擎会把 www.example.com 和 example.com 视为两个站点,必须选一个主域名并 301 统一,否则权重被拆散。
# 让 example.com 跳到 www.example.com
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
return 301 https://www.example.com$request_uri;
}
特别注意:跳转方向和证书要对应。如果证书只签了 www,用户访问裸域名时可能因为证书不匹配而报警告,所以证书最好把两个域名都列入(SAN)。
场景三:整个域名迁移
把 old.com 的所有内容迁到 new.com,除了页面路径,还要考虑旧链接。整站跳转后,旧站的 sitemap 和 robots 也应更新,并在搜索引擎站长平台做"地址变更"操作。
server {
listen 80;
server_name old.com www.old.com;
return 301 https://new.com$request_uri;
}
改完配置要测试语法再重载,避免直接重启导致短暂不可用:
# 测试配置语法
sudo nginx -t
# 语法通过后平滑重载(不中断连接)
sudo systemctl reload nginx
# 若用 dnf/yum 安装且是 RHEL/CentOS/Rocky,同样用 systemctl
# Debian/Ubuntu 也是 systemctl reload nginx
四、循环重定向怎么排查
循环重定向的症状是浏览器报"重定向次数过多"(ERR_TOO_MANY_REDIRECTS),页面打不开。原因通常是两条规则互相跳,或者条件判断把重定向结果又导回了原地。
第一步用 curl -I -L 看跳转链。-I 只看响应头,-L 跟随跳转,加上 -v 能看到每一跳的 Location。
# 看跳转链,重点关注每次返回的 Location
curl -I -L -s -o /dev/null -w "%{http_code} %{url_effective}\n" http://example.com/
# 更详细:打印每一跳
curl -sIL http://example.com/ | grep -iE '^(HTTP|Location)'
# 限制最多跟随 5 跳,防止无限循环卡住
curl -sIL --max-redirs 5 http://example.com/
如果输出的 Location 在两个地址间反复横跳,就找到循环了。
常见循环原因:
- HTTPS 跳转条件写错。 用
if ($scheme = http)判断时,若前面已经通过X-Forwarded-Proto之类变量误判,或 CDN 回源时协议被改写,会导致"跳到 HTTPS 又被判成 HTTP 再跳回去"。正确做法是 HTTP 和 HTTPS 用不同的server块,而不是在同一个块里靠if判断。 - CDN 与源站双重跳转打架。 源站和 CDN 都配了 HTTPS 跳转,回源时协议不一致,来回跳。要确认 CDN 回源用的是 HTTP 还是 HTTPS,并让源站对回源请求放行。
location规则冲突。 两条rewrite互相指向对方,或者某个location的规则把请求又交回给会跳转的location。检查规则顺序和 flag。- 泛解析导致误匹配。
server_name _;或通配匹配把不该处理的域名也接住了,产生错误跳转。
排查循环时,一个实用技巧是临时把某条规则改成返回固定文本(比如 return 200 "hit-rule-1";),看请求到底命中了哪条规则,逐段定位。
五、配置 301 的注意事项
第一,方向要单一,只保留一条链。A→B、B→C 不如直接 A→C,跳转链越长,权重传递越弱,用户等待越久。理想状态是任何旧地址一跳到位。
第二,不要用 301 做临时跳转。临时维护、灰度切流应该用 302,因为 301 会被浏览器和搜索引擎长期记住,改回来很麻烦。
第三,区分大小写和查询参数。Nginx 的 server_name 和正则默认区分大小写,rewrite 的正则要留意 ~(区分大小写)和 ~*(不区分)的区别。迁移带参数的动态链接时,务必用 $request_uri 保留查询串,否则用户带着 ?id=123 访问会丢失参数。
第四,证书和跳转目标要一致。跳转到 HTTPS 但证书不匹配,用户会看到安全警告,等于把用户挡在门外。
第五,改配置前备份,改完 nginx -t 再 reload。生产环境的配置改动有风险,尤其在流量高峰期,建议先在测试环境验证,并准备好回滚(备份旧配置,出问题立即恢复)。
六、怎么验证 301 生效了
配置好之后,除了 curl,还要从搜索引擎视角确认。几个检查点:
# 确认状态码是 301 而不是 302
curl -I http://example.com/
# 期望看到:HTTP/1.1 301 Moved Permanently
# 确认 Location 指向正确
curl -I http://example.com/ | grep -i location
# 确认最终落地页正常(200)
curl -I -L http://example.com/ | tail -1
# 批量检查老 URL 是否都正确跳转(从日志或 sitemap 取一批老路径)
for p in /old-page-1 /old-page-2 /blog/old-post; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://new.example.com$p")
echo "$p -> $code"
done
状态码应该是 301,Location 指向新地址,最终落地页返回 200。做到这三点,说明跳转配置基本没问题。最后别忘了到搜索引擎站长平台提交新域名的 sitemap,并(如支持)使用"地址变更"工具,加速收录迁移。
常见问题
Q:301 配好后,什么时候能改回 302?
技术上随时可以改,但浏览器已经缓存了 301 的用户会继续跳。可以改用 302 并接受部分用户仍跳转,等缓存过期后自然恢复。所以配置前要想清楚。
Q:return 301 和 rewrite ... permanent 该用哪个?
固定目标地址(整站/整域名跳转)优先 return 301,简单高效;需要按路径规则批量改写时用 rewrite ... permanent。二者对 SEO 等价,差异在写法灵活性。
Q:跳转后查询参数丢了怎么办?
用 $request_uri 而不是 $uri。$request_uri 保留原始路径和查询串,$uri 是规范化路径且不带参数。
Q:怎么确认不是 302?
curl -I 看状态行,301 写的是 Moved Permanently,302 是 Found。别只看浏览器,浏览器可能不显示状态码。
Q:整站换域名,旧域名要保留多久?
建议至少保留一年甚至更久,因为外链、用户收藏、搜索引擎索引都需要时间更新。跳转保持长期有效,成本很低,不要急着关停旧域名。
总结
301 是永久重定向,会把权重和收录传到新地址,和 302(临时,不传权重)有本质区别,且会被浏览器长期缓存,配错后不易改回。
Nginx 里整站跳转用 return 301 配 $request_uri 保留路径参数,批量路径改写用 rewrite ... permanent。
循环重定向用 curl -I -L 看跳转链定位,常见根因是 HTTPS 判断写错、CDN 与源站双重跳转打架、location 规则冲突。
配置原则:跳转链尽量短(一跳到位)、临时场景用 302、注意证书与目标一致、改前备份并 nginx -t 后再 reload。
验证要看状态码为 301、Location 正确、落地页 200,并在站长平台提交新 sitemap 和地址变更,加速收录迁移。
企业QQ咨询




