做站点时间久了,你会发现一个很邪门的现象:明明是同一个网站,在搜索引擎里却能搜出两个地址,http://www.domain.com和http://domain.com各占一条,站内文章也被人分头引用。这不是域名解析坏了,而是典型的 URL 不规范问题。要解决它,核心就是做一次 301 重定向,把 http://www.domain.com 的访问统一导向 http://domain.com。
这篇内容适合正准备上线的个人站长,也适合接手老项目的运维。不管你是要清理历史遗留的重复入口,还是想在新站点上线第一天就把规范做好,都可以按下面的思路走一遍。我会先把“选哪个域名”的账算清楚,再讲跳转背后的 HTTP 原理,然后给出 Nginx、Apache、IIS 三种主流环境的完整配置,最后说验证方法和上线后的收尾清单。
1. 先站队:www 和裸域名,到底哪一个才是你的归宿
很多教程上来就甩配置,但我建议你先想清楚方向。方向错了,后面的 301 就是白干,甚至会把权重从一个坑挪到另一个坑。
1.1 两种地址在浏览器和服务器的眼里完全不同
从普通用户的角度看,http://www.domain.com和http://domain.com无非差了几个字母。但在浏览器、服务器和搜索引擎眼里,这是两个不同的主机名,意味着两套完全独立的资源入口。
先说 Cookie。如果你不显式设置Domain=domain.com,浏览器会按请求的主机名分别存储 Cookie。用户从www.domain.com登录,跳到domain.com之后登录态就丢了,需要重新登录。这在高并发站点上尤其烦人,用户以为是系统出 bug,实际上是跨子域 Cookie 没打通。
再说静态资源和 CDN。很多 CDN 厂商支持泛解析,裸域名比www子域更容易做分发配置;反过来,也有不少团队习惯把www作为一个稳定的子域,单独接 WAF、单独配缓存,方便和主站解耦。
最关键的还是 SEO。搜索引擎对“主机名”是敏感的,www和非www会被当成两个站点分别收录。外链一部分指向www,一部分指向裸域名,权重就散了。这也是为什么会出现“正式站点收录少、多出来的镜像页面反而排在前面”的怪事。
1.2 不同体量站点的选型建议
选哪个没有绝对标准,但我给你一个判断模型:
- 个人博客、SaaS 后台、API 服务、工具类站点,优先裸域名。理由很简单:输入短、配置少、后续用 CDN 泛解析也省事,而且内部逻辑里不需要再区分“主站”和“WWW 子域”。
- 企业官网、电商、会员体系复杂的站点,优先
www。www是一个稳定的子域,可以单独做缓存策略,也可以只给它配严格的鉴权,而裸域名负责跳转。很多企业邮箱、SSL 证书的历史配置也围绕www展开,迁移成本更低。 - 如果团队没有明确偏好,我建议按“用户输入成本最低”的原则来:面向大众的内容站,裸域名更友好;需要给子域预留扩展空间的业务系统,
www更稳妥。
一旦选定,就用 301 把另一个地址永久甩掉。别今天跳 www,明天跳裸域名,搜索引擎被反复折腾之后,会花很长时间重新评估你的站点权重。
2. 跳转的底牌:301状态码、Location和URL规范化
配置本身不难,难的是理解为什么 301 才是唯一正解。很多人图省事,用了 302,结果几个月后权重依然没转过去。
2.1 301和其他状态码的差异
先看一张表,记住每个状态码的作用:
| 状态码 | 含义 | 是否保留请求方法 | 对SEO的影响 |
|---|---|---|---|
| 301 Moved Permanently | 永久移动 | 通常会变成GET | 权重转移,浏览器和搜索引擎都会缓存结果 |
| 302 Found | 临时移动 | 通常会变成GET | 权重不转移,只当作临时跳转 |
| 307 Temporary Redirect | 临时重定向 | 保留POST等方法 | 权重不转移,适合表单场景 |
| 308 Permanent Redirect | 永久重定向 | 保留POST等方法 | 权重转移,但兼容性不如301 |
站点换主域名、统一 www 入口、清理重复页面,这些都是永久性操作,所以必须用 301。304 这种“Not Modified”完全是另一个场景,千万别混在一起。
用 302 的典型后果是:搜索引擎会认为你只是临时把用户指引到别处,排名权重还挂在老地址上,过段时间又继续抓取老 URL,甚至出现新旧两版同时被收录的情况。我见过不止一个团队因为把 redirect 写成 302,导致流量迟迟没有迁移到 HTTPS 新站,最后只能全量返工。
2.2 一个重定向响应背后的真实流程
当用户访问http://www.domain.com/abc时,服务器返回的完整响应大致长这样:
HTTP/1.1 301 Moved Permanently Location: http://domain.com/abc Content-Type: text/html Content-Length: 0浏览器和搜索引擎爬虫看到Location字段,就会发起第二次请求,去访问http://domain.com/abc。用户感知到的是一次短暂跳转,但对爬虫来说,这是一次“旧地址作废,新地址接收全部信号”的声明。
这里有两个细节要注意。第一,Location必须写完整,不能只写/abc,否则浏览器不知道要去哪个主机名。第二,响应里不要放太多 HTML 内容,301 的正文通常留空,省流量也避免被误解析。
2.3 Canonical标签是第二道保险
服务器层面的 301 是最可靠的,但站点里难免有历史页面、第三方接口、临时活动页没走重定向规则。此时rel=canonical能兜底。
在页面<head>里加上:
<link rel="canonical" href="http://domain.com/文章路径" />意思是告诉搜索引擎:这个页面即使被访问了,真正的规范地址也是指向裸域名那个。配合服务器 301,双保险的效果最稳。
但要注意,canonical 是“建议”,搜索引擎有权利不采纳;而 301 是“命令”。所以 canonical 永远只能作为辅助,不能替代真正重定向。
3. 三种主流服务器配置,把www地址稳准狠地甩回主域
现在进入实操环节。我会按 Nginx、Apache、IIS 三种情况分别给配置,并说明为什么这么写。
3.1 Nginx:两个server块,比if判断干净得多
Nginx 下最常见的写法是给www.domain.com单独建一个 server 块,只干一件事:返回 301。
server { listen 80; server_name www.domain.com; return 301 http://domain.com$request_uri; } server { listen 80; server_name domain.com; root /var/www/domain; index index.html; location / { try_files $uri $uri/ =404; } }这段配置的核心是$request_uri。它是一个内置变量,保存了用户请求的完整路径和查询参数,比如/article?id=1。把它拼到目标域名后面,就能保证从www.domain.com/article?id=1跳转到domain.com/article?id=1,路径、参数都不丢。
为什么不推荐在同一个 server 块里写if ($host = www.domain.com)?因为 Nginx 的if是 rewrite 模块里的“黑魔法”,指令执行顺序和直觉不一致,容易引发莫名其妙的 500、404。独立 server 块的写法逻辑清晰,也方便以后单独给www加 HSTS 或访问日志,维护成本最低。
如果以后要换成“裸域名跳转到 www”,只需把两个 server 块里的域名对调,逻辑一模一样。
3.2 Apache:VirtualHost和.htaccess两条路
Apache 环境优先在 VirtualHost 里配置。找到www.domain.com对应的站点配置:
<VirtualHost *:80> ServerName www.domain.com Redirect 301 / http://domain.com/ </VirtualHost>Redirect 301 / http://domain.com/的意思是:把当前虚拟主机下所有路径,301 到http://domain.com/下对应路径。Apache 会自动把/article?id=1拼接过去,不需要额外变量。
如果你只有 .htaccess 的修改权限,比如虚拟主机面板环境,可以使用 mod_rewrite:
RewriteEngine On RewriteCond %{HTTP_HOST} ^www\.domain\.com$ [NC] RewriteRule ^(.*)$ http://domain.com/$1 [L,R=301]这里判断的是HTTP_HOST,而不是SERVER_NAME。原因是SERVER_NAME在部分 Apache 配置里可能被ServerAlias或默认虚拟主机覆盖,导致判断结果不准确;HTTP_HOST直接取自请求头,最接近用户真实访问的地址。[NC]表示大小写不敏感,[L,R=301]表示这是最终规则并返回 301。
3.3 IIS:URL Rewrite的web.config写法
Windows 服务器上用 IIS 的话,首先要安装 URL Rewrite 模块。安装完成后,可以直接在站点根目录的web.config里加规则:
<rewrite> <rules> <rule name="Remove WWW" stopProcessing="true"> <match url="(.*)" /> <conditions> <add input="{HTTP_HOST}" pattern="^www\.domain\.com$" /> </conditions> <action type="Redirect" url="http://domain.com/{R:1}" redirectType="Permanent" /> </rule> </rules> </rewrite>关键点有两个。第一个是<add input="{HTTP_HOST}" pattern="^www\.domain\.com$" />,它只匹配以www.domain.com为开头的请求,不会误伤其他子域。第二个是<action type="Redirect" redirectType="Permanent" />,Permanent对应 HTTP 301,千万别选Found,那是 302。
如果服务器上同一个站点绑定了一堆域名,只想对其中一个做跳转,stopProcessing="true"可以保证这条规则匹配后直接结束,不再执行后续规则。
3.4 不得已时的兜底:面板转发和前端跳转
有些托管面板提供“域名转发”功能,原理多半是 meta refresh 或 iframe,不推荐用在正式站点。meta refresh 虽然能跳,但搜索引擎会把它当成一种弱信号,权重继承效果远不如 301。
如果连服务器配置都改不了,只能临时用 JavaScript 跳转:
<script> if (window.location.host === 'www.domain.com') { window.location.replace('http://domain.com' + window.location.pathname + window.location.search); } </script>这段代码我强调过很多次:是“最后手段”。爬虫可能不执行 JS,用户也可能在脚本加载前就关闭页面,用它做长期方案必然出问题。
4. 动手之前必须打理好的两个前置条件:DNS和HTTPS
服务器跳转规则写得再好,DNS 和证书没做好,照样会翻车。这一节说的是配置之前最容易踩的两个坑。
4.1 解析记录:A记录与CNAME的搭配方式
你要让两个域名都能被访问,解析记录至少得这样配:
| 主机记录 | 记录类型 | 记录值 | 说明 |
|---|---|---|---|
| domain.com | A | 服务器IP | 裸域名指向服务器 |
| www | CNAME | domain.com | www 指向裸域名 |
这里有个常见误区:很多人把www的 CNAME 指向裸域名后,就觉得“反正都会到同一台服务器”。确实,在服务器层面两个域名解析到同一入口,但这只是第一步,服务器还必须知道要把www的请求跳过去。DNS 只负责解析,不负责301,这是两层完全不同的逻辑。
另外要注意,根域名本身不能设置 CNAME,这是 DNS 协议的限制。如果服务商支持 ALIAS 或 ANAME 记录,可以例外,但大多数情况下裸域名直接用 A 记录最稳妥。如果以后服务器迁移 IP,只需要改裸域名的 A 记录,www的 CNAME 会自动跟随。
4.2 HTTPS下的重定向链路,先证书后跳转
现在做站点,基本上绕不开 HTTPS。如果你既跳转域名又跳协议,整个过程会变成:http://www.domain.com先跳到http://domain.com,再通过另一条规则跳到https://domain.com。白白多一跳,用户体验和爬虫效率都会受影响。
更稳妥的做法是合并成一条链路:从http://www.domain.com直接 301 到https://domain.com。为此,证书必须同时覆盖www.domain.com和domain.com。有一种典型坑是只申请了*.domain.com通配符证书,然后发现通配符不匹配裸域名,https://domain.com打开就是证书错误。
Nginx 下推荐这样拆:
# 所有80端口请求,统一跳到HTTPS的裸域名 server { listen 80; server_name www.domain.com domain.com; return 301 https://domain.com$request_uri; } # www的443端口,只做跳转 server { listen 443 ssl; server_name www.domain.com; ssl_certificate /path/domain.pem; ssl_certificate_key /path/domain.key; return 301 https://domain.com$request_uri; } # 真正的网站本体 server { listen 443 ssl; server_name domain.com; ssl_certificate /path/domain.pem; ssl_certificate_key /path/domain.key; root /var/www/domain; index index.html; }这里最容易被忽略的是:www的 443 server 块也要配置正确的证书,否则用户直接输入https://www.domain.com时,TLS 握手阶段就会告警,根本没机会执行后面的 301。证书文件用同一个包含 SAN 的证书即可。
4.3 本地hosts模拟测试环境
改线上配置之前,先在本地模拟解析环境,可以省去很多来回折腾。操作方法很简单:
Linux/macOS 编辑/etc/hosts,Windows 编辑C:\Windows\System32\drivers\etc\hosts,加入两行:
127.0.0.1 www.domain.com 127.0.0.1 domain.com修改之后,本地访问这两个域名都会指向本机。这样你可以在不迁移线上、不修改公网 DNS 的情况下,把完整的跳转链路验证一遍。测试完记得删掉这两行,否则会一直干扰本地开发。
5. 上线验证与排错:别让重定向在你看不见的地方翻车
配置写完不等于工作做完。我见过太多“配置看起来没问题,但线上就是跳不对”的案例,所以专门留一节讲验证和排错。
5.1 curl三板斧:状态码、Location、整条链路
第一板斧,看单次跳转的状态码和 Location:
curl -s -o /dev/null -w "status:%{http_code}\nlocation:%{redirect_url}\n" http://www.domain.com预期输出是:
status:301 location:http://domain.com/如果你看到 200,说明服务器根本没执行跳转;看到 302,说明规则写错了状态码。
第二板斧,测试路径和参数是否保留:
curl -s -o /dev/null -w "status:%{http_code}\nlocation:%{redirect_url}\n" http://www.domain.com/article?id=123预期location应该是http://domain.com/article?id=123。如果跳过去变成了http://domain.com/,说明配置里用了不带路径的跳转目标,要回头检查是不是漏了$request_uri或$1。
第三板斧,用-L跟随整条链路:
curl -sIL http://www.domain.com这条命令会一路跟到最终页面,适合检查有没有多余的中间跳。正常应该是:301到裸域名,再返回200。如果看到两次 301,就要检查是不是 http 跳 https、https 又跳回 http,形成了隐形绕路。
5.2 浏览器和第三方工具的检查视角
浏览器 F12 的 Network 面板是最直观的验证方式。打开开发者工具,勾选 Preserve log,然后访问http://www.domain.com,观察第一个请求:
- 状态码是 301,说明跳转规则生效;
- Headers 里的 Location 指向
http://domain.com/,说明跳转目标正确; - 请求链表里只有一次 301 + 一次 200,说明链路干净。
如果浏览器直接显示“重定向次数过多”,基本就是循环了。还有一种情况是 HSTS 已开启,浏览器会先把http://www自动升级成https://www再发给服务器,导致你在浏览器的 Network 面板里看不到最初的 80 端口 301。此时用 curl 测试最准确,因为 curl 不受 HSTS 策略影响。
5.3 高频故障:循环、丢路径、丢参数、HTTPS混合内容
我整理了实际操作里最常见的问题,对照着排查会快很多:
| 故障现象 | 可能原因 | 解决办法 |
|---|---|---|
| 重定向循环 | 规则A跳到B,B的规则又跳回A | 逐个 server 块检查server_name和跳转目标是否冲突 |
| 跳到首页,路径丢失 | 用了固定/而不是$request_uri或$1 | 改成保留路径的变量写法 |
| 查询参数丢失 | 重定向目标带了?,但没拼接 query | 用$request_uri,或在 Apache 里加QSA标志 |
| 页面出现“不安全内容”提示 | 站内图片、脚本仍用 http 绝对地址 | 全局搜索替换为 https 或相对协议地址 |
| 跳转目标证书错误 | 证书没有覆盖目标域名 | 申请包含 SAN 的证书,同时覆盖 www 和裸域名 |
排查循环时,有一个土办法:把跳转链路的每一步写成日志。Nginx 可以在www的 server 块里开独立 access log,看到底是哪个请求被反复跳。大多数循环问题出在“http 和 https 的 server_name 互相打架”,比如 http 的规则跳 https 的裸域名,而 https 的裸域名又有一条规则跳回 http。
6. 跳转之后的收尾:内链、后台和观察期
服务器 301 上线只是开胃菜,后面的收尾工作决定这次规范化能走多远。
6.1 把站内所有绝对地址换成统一入口
很多人以为服务器做了 301,站内链接就可以继续乱写。实际上,如果某个页面的源码里还带着http://www.domain.com/xxx,用户点击时会先产生一次额外跳转,白白增加延迟;搜索引擎抓取也会多绕一跳。
上线后要清理的东西包括:
- 数据库文章内容里的历史绝对链接;
- 主题模板里写死的
site_url或图片地址; robots.txt里的Sitemap地址;- 提交过的
sitemap.xml中所有 URL。
建议在程序代码层面对外输出链接时统一使用一个“站点主域”常量,后续要改域名只改一处。这也是为什么很多框架推荐用相对路径或协议相对路径//domain.com/xxx,能少踩很多坑。
6.2 站长工具和统计平台的同步设置
重定向完成后,需要到各平台申报变更,否则搜索引擎需要更长的时间来消化:
- Google Search Console:添加裸域名和 www 两个版本,使用“地址更改”工具把 www 的属性指向裸域名。
- 百度搜索资源平台:提交站点改版规则,按照平台要求填写源地址和目标地址。
- 统计工具:确认默认域名是裸域名,否则后续 PV、UV 数据会劈成两份。
- 第三方统计:检查历史报表里 iframe 嵌的是哪个地址,统一替换。
这些操作不是可选项。搜索引擎抓取有一个“信任周期”,主动申报能大幅缩短权重转移的等待时间。
6.3 观察节奏与回退预案
我一般用三个时间窗口观察:
- 第 1~3 天:盯访问日志里的 404、500,重点看是不是有历史外链带着老路径过来,被跳转规则错误地丢弃了。
- 第 1~2 周:看站长平台的抓取异常、索引变化,此时旧地址的收录量应该开始下降,新地址的索引量逐步上升。
- 第 1 个月以上:看核心关键词排名和自然流量曲线。理论上前两周会有小幅波动,这是正常现象。
如果真出现必须回退的情况,比如业务上高层突然要求保留 www 作为主入口,不要慌。把前面配置里的目标域名整体对调,同时重新提交站长工具的改版规则即可。但我必须提醒一句:反复横跳对权重伤害极大,每次改版都意味着重新积累信任,没必要就别玩这个。
我自己的习惯是,在切换前一天把所有配置在测试环境完整执行一遍,然后列一个 curl 测试清单:首页、带路径页面、带参数页面、HTTPS 入口,逐个跑一遍再正式上线。这个小习惯帮我避开过好几次线上跳转事故,也推荐你试试。