原创

Nginx 实现域名跳转的几种方式详解

为什么“域名跳转”不能只会一种写法

在 Nginx 里做域名跳转,看起来只是把一个域名导到另一个域名,但实际场景差别很大:

  • 旧域名迁移到新域名
  • www 和裸域统一
  • http 强制跳 https
  • 多个历史域名统一收口
  • 某些路径跳转,某些路径保留

真正容易出问题的,不是语法本身,而是跳转类型选错、规则顺序写错、Host 和 URI 丢失、重定向层级过多。这些问题一旦上线,轻则 SEO 受影响,重则直接形成重定向循环。

下面把 Nginx 中几种常见的域名跳转方式拆开讲清楚。


301 和 302 先分清楚

在写配置之前,先明确两类最常见的重定向状态码:

状态码 含义 适用场景
301 永久重定向 域名永久迁移、www 统一、HTTP 跳 HTTPS
302 临时重定向 临时活动页、灰度切换、短期测试跳转

选择建议

  • 长期固定规则:优先用 301
  • 短期临时跳转:用 302
  • 不确定是不是长期规则时,别急着上 301。因为浏览器和搜索引擎会缓存永久跳转,后面改起来会比较麻烦

很多人本地测试觉得“能跳就行”,但线上不是这样。 301 一旦被缓存,你改配置后未必马上能看到结果,这也是排查时经常让人误判的地方。


方式一:用 return 做整站域名跳转

这是最推荐的方式,也是最干净的一种。

场景:旧域名整体跳到新域名

比如把 old.example.com 全量迁移到 new.example.com

server {
    listen 80;
    server_name old.example.com;
    return 301 https://new.example.com$request_uri;
}

说明

  • 301:永久跳转
  • https://new.example.com:目标域名
  • $request_uri:保留原始请求路径和参数

比如用户访问:

http://old.example.com/user/list?id=10

会跳转到:

https://new.example.com/user/list?id=10

为什么推荐 return

因为它有几个明显优势:

  • 配置简单,意图清晰
  • 性能比 rewrite 更直接
  • 不容易写出复杂且难维护的规则
  • 做整站跳转时,几乎就是最合适的选择

如果你的需求只是“这个域名整个换掉”,优先考虑 return,不要一上来就写 rewrite


方式二:用 rewrite 实现域名跳转

rewrite 更灵活,但也更容易写乱。

示例:将旧域名跳转到新域名

server {
    listen 80;
    server_name old.example.com;

    rewrite ^/(.*)$ https://new.example.com/$1 permanent;
}

说明

  • ^/(.*)$:匹配请求路径
  • https://new.example.com/$1:拼接目标地址
  • permanent:表示 301
  • 如果写成 redirect,则表示 302

对应关系

参数 实际含义
permanent 301
redirect 302

这种方式适合什么场景

rewrite 更适合做带规则匹配的路径级跳转,比如:

  • 旧 URL 结构迁移到新 URL 结构
  • 某类路径批量改写
  • 根据正则提取参数进行重定向

例如:

server {
    listen 80;
    server_name old.example.com;

    rewrite ^/article/(.*)$ https://new.example.com/posts/$1 permanent;
}

这表示:

http://old.example.com/article/123

跳转为:

https://new.example.com/posts/123

不建议滥用 rewrite

因为很多整站跳转场景,其实不需要正则。 你用 rewrite 也能做,但维护成本更高,而且后面别人接手时不容易一眼看懂。


方式三:基于 server_name 做多个域名统一跳转

这是线上最常见的收口方式之一。

场景:多个旧域名统一跳到一个主域名

server {
    listen 80;
    server_name a.example.com b.example.com c.example.com;
    return 301 https://www.example.com$request_uri;
}

这表示:

  • a.example.com
  • b.example.com
  • c.example.com

都会跳到:

https://www.example.com/原始路径

适用场景

  • 历史域名合并
  • 品牌升级后的域名统一
  • 测试域名、备用域名收口
  • 多个二级域名统一导流到主站

这种方式的好处是简单直接,尤其适合整理历史遗留配置。


方式四:www 和非 www 域名互相跳转

这是非常经典的一类需求。

你必须先决定,系统最终要统一成哪一种:

  • www.example.com
  • example.com

不要两种都放着能访问。这样会带来:

  • SEO 权重分散
  • Cookie 域问题变复杂
  • 对外链接不统一
  • 排查问题时 Host 不一致

1)非 www 跳转到 www

server {
    listen 80;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

2)www 跳转到非 www

server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

一般怎么选

没有绝对标准,但实践里通常是:

  • 企业官网、门户类站点:常见统一到 www
  • 接口服务、后台服务:很多统一到裸域或专用子域名

重点不是选哪一个,而是只保留一个对外主域名


方式五:HTTP 强制跳转到 HTTPS

这个配置几乎已经是标配。

配置示例

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

说明

  • $host:保留当前访问的域名
  • $request_uri:保留路径和参数

比如:

http://www.example.com/login?redirect=/home

会跳到:

https://www.example.com/login?redirect=/home

为什么这里常用 $host

因为当你同时接收多个域名时:

server_name example.com www.example.com;

使用 $host 可以保持当前请求的域名不变,只做协议升级。

如果你还要统一主域名

那就不要只写协议跳转,而是一步到位,直接收口。例如统一到 https://www.example.com

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}

这样可以避免两次跳转:

  • 第一次:HTTP → HTTPS
  • 第二次:非 wwwwww

最好一跳到位。


方式六:按路径做域名跳转

有些需求不是整站迁移,而是某个模块迁走了。

示例:/blog 跳到另一个域名

server {
    listen 80;
    server_name example.com;

    location /blog/ {
        return 301 https://blog.example.com$request_uri;
    }
}

访问:

http://example.com/blog/post-1

会跳到:

https://blog.example.com/blog/post-1

如果你不想保留 /blog

比如希望:

http://example.com/blog/post-1

跳到:

https://blog.example.com/post-1

可以这样写:

server {
    listen 80;
    server_name example.com;

    location /blog/ {
        rewrite ^/blog/(.*)$ https://blog.example.com/$1 permanent;
    }
}

这类场景里,rewrite 就比 return 更合适,因为你确实需要改路径结构。


方式七:使用独立的默认站点兜底跳转

有时候你希望所有未明确配置的域名都跳转到某个统一站点。

示例

server {
    listen 80 default_server;
    server_name _;
    return 301 https://www.example.com$request_uri;
}

适用场景

  • 非法访问统一收口
  • 测试环境中做简单兜底
  • 历史 DNS 残留流量导流

但这个配置要谨慎

因为它会接住所有没有命中其他 server_name 的请求。 如果你的机器上托管多个站点,兜底跳转写得太随意,可能会把本不该跳的请求也导走。


returnrewrite 该怎么选

很多人纠结的其实是这个。

对比项 return rewrite
配置复杂度
性能 更直接 略复杂
可读性 一般
是否支持正则改写 不擅长 擅长
适合整站跳转 非常适合 能做但不优先
适合路径重写 一般 更适合

实战建议

  • 整站域名跳转:优先 return
  • HTTP 跳 HTTPS:优先 return
  • www 统一:优先 return
  • 复杂路径迁移:使用 rewrite

一句话总结:

只要不是必须改路径结构,就优先用 return


生产环境里经常踩的坑

1. 忘了带 $request_uri

错误写法:

return 301 https://www.example.com;

这样会导致用户访问任意路径,都只跳到首页。

例如:

http://old.example.com/order/detail?id=100

最终变成:

https://www.example.com

原始路径和参数全部丢失。

正确写法

return 301 https://www.example.com$request_uri;

2. 配置导致重定向循环

比如你在 HTTPS 站点里又写了一条“跳 HTTPS”的规则,或者 A 域名跳 B,B 又跳回 A。

常见现象:

  • 浏览器提示 ERR_TOO_MANY_REDIRECTS
  • 接口请求一直 301/302
  • 登录流程直接失效

排查思路

先用 curl 看响应头:

curl -I http://example.com

如果要跟踪多次跳转:

curl -IL http://example.com

Location 是否出现来回跳。


3. HTTP 和 HTTPS 各写一套,结果规则不一致

比如:

  • 80 端口跳到 www
  • 443 端口却没有统一 Host
  • 或者 80 上保留 URI,443 上丢 URI

结果就是不同入口行为不一致,用户看起来像“有时候能打开,有时候跳错地址”。

比较稳妥的做法是:

  • 80 端口:只负责收口和协议升级
  • 443 端口:只提供最终站点服务
  • 所有入口都尽量一步跳到最终目标地址

4. 把跳转和反向代理混在一起

有些配置会写成这样:

location / {
    proxy_pass http://backend;
    rewrite ^/(.*)$ https://www.example.com/$1 permanent;
}

这种写法通常是有问题的,或者至少非常难维护。 因为请求处理阶段不同,规则优先级也不同,后面排查时很容易搞不清到底是重定向了,还是转发到上游了。

建议

  • 要跳转,就明确用 returnrewrite
  • 要转发,就明确用 proxy_pass
  • 尽量不要把两种意图揉在同一个位置里

5. 多层跳转影响性能和收录

比如用户访问一次,经历了:

  1. http://example.com
  2. 跳到 https://example.com
  3. 再跳到 https://www.example.com
  4. 最后又跳到 https://www.example.com/home

虽然最终能打开,但中间多了好几次往返。

这会带来:

  • 首次访问更慢
  • 爬虫抓取效率降低
  • 用户排查问题更困难

最理想的是:

从任意入口,一次 301 直接到最终 URL。


一组比较实用的标准写法

下面给一套比较常见、也比较顺手的配置思路: 把 http://example.comhttp://www.example.com 全部统一到 https://www.example.com

1)HTTP 入口统一跳转

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}

2)HTTPS 下裸域继续收口

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    return 301 https://www.example.com$request_uri;
}

3)HTTPS 主站提供正式服务

server {
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
}

这一套的特点

  • 所有入口最终统一到一个主域名
  • 规则职责明确
  • 不容易产生循环
  • 后续接 CDN、做 SEO、配证书都更清晰

验证跳转是否生效

修改配置后,不要直接重启,先检查语法。

1)检查配置

nginx -t

2)重新加载配置

nginx -s reload

或者:

systemctl reload nginx

3)用 curl 验证

查看响应头:

curl -I http://example.com/test?a=1

预期应看到类似:

HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/test?a=1

如果要确认多层跳转链路:

curl -IL http://example.com/test?a=1

这个命令非常实用,能快速看出有没有多跳、循环、路径丢失等问题。


总结

Nginx 实现域名跳转,常见方式主要有:

  • return 做整站跳转
  • rewrite 做规则化路径跳转
  • 基于 server_name 做多域名收口
  • www 与非 www 统一
  • HTTP 强制跳 HTTPS
  • 指定路径跳转到新域名
  • default_server 做兜底跳转

真正实战里,建议记住两条:

  1. 整站跳转优先用 return
  2. 目标地址尽量一次跳到最终态

语法不是最难的,最难的是把规则写得清晰、可维护、不会互相打架。 尤其当站点同时涉及多个域名、HTTPS、反向代理和历史路径兼容时,跳转配置越“聪明”,后面往往越难排查。


正文到此结束
评论插件初始化中...
Loading...