ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

彻底搞懂http版本301重定向:从Nginx到Apache的www域名收拢全攻略

彻底搞懂http版本301重定向:从Nginx到Apache的www域名收拢全攻略 看到这个标题我第一反应就是这几乎是每个站长的必修课。只要域名带 www 和不带 www 都能访问你迟早会遇到要不要做重定向的纠结尤其是当你发现网站带了 www 的版本和不带 www 的版本都能打开时搜索引擎会把你当两个网站看用户分享出去的链接也稀里糊涂。这篇我就拿“重定向 http://www.domain.com 到 http://domain.com”这个最典型的场景把我踩过的坑、验证过的配置、排查思路完整写出来帮你一次性把这类需求彻底搞定。1. 项目概述与核心需求解析1.1 这个需求到底在解决什么问题先说清楚这个需求本身。你现在有一个站点用户既可以输入http://www.domain.com访问也可以输入http://domain.com访问。短期看好像没什么毛病但时间一长问题就堆出来了。最典型的是搜索引擎索引分裂www和不带www的页面会被当成两个不同的站点分别收录权重被稀释快照也可能乱掉。另外一个容易忽略的问题是 Cookie 域如果你登录态种在.domain.com上两个域名还能共享如果种在www.domain.com或domain.com上用户从一个域名跳到另一个域名登录状态直接丢失体验非常割裂。所以这个重定向的本质就是把所有访问统一收拢到一个“主域名”上。标题里写的是http://www.domain.com重定向到http://domain.com也就是说这是一次“去掉 www”的收拢目标域名是不带www的裸域。平时我还经常遇到反过来的场景把裸域重定向到www带头的域名技术上完全一样只需要把判断条件和目标地址对调。1.2 适合谁来参考这套方案如果你是自己用宝塔面板或手工配置 Nginx 的个人站长或者是公司里维护 Apache、IIS 的传统网站项目的运维这篇都很值得看完。我会按环境分门别类给出可直接复制的配置同时把原理讲明白这样你遇到变体需求比如要带https、要保留某个固定路径不跳转也能自己改。这套东西的安全性和稳定性要求不低因为重定向规则一旦写错轻则页面打不开重则造成死循环把整个站点搞挂。所以我不光给你配置还会在最后专门用一整节讲怎么排查这样你照着做出来不是“看似能跑”而是真正稳定的能上线。2. 动手前必须先想清楚重定向方案怎么选2.1 HTTP 状态码对比301、302、307、308 用哪个很多人配置重定向时只关心“跳不跳得过去”从来不关心响应状态码这是最容易埋雷的地方。标题里的场景属于网址形态的永久性迁移应该用 301 永久重定向。301 会告诉浏览器、搜索引擎和各类代理这个地址已经彻底失效以后都请用新地址。这样搜索权重才能平滑传递这也是 SEO 圈公认的标准操作。那 302 呢临时重定向语义上是“这次先跳过去但原地址还有效”。如果你在这里用 302搜索引擎可能不会把权重转移到目标地址甚至可能继续索引旧地址。308 是 301 的升级版区别在于它要求重定向时保持请求方法和请求体适合 POST 接口迁移我们做页面跳转一般用不上。307 同理保持方法的基础上做临时跳转常见于 A/B 测试和临时的维护页场景也不适合长期收拢域名。所以我给你的核心结论是静态页面和整站收拢一律用 301临时活动页或接口切流才用 302、307、308。状态码类型对搜索引擎权重典型场景301永久传递权重域名收拢、地址永久变更302临时不推荐长期使用临时活动页跳转307临时且保持方法不传递权重临时接口切流308永久且保持方法传递权重POST 接口永久迁移2.2 确认目标域名保留 www 还是去掉 www动手改配置之前先冷静做一个小决策你的主域名到底选哪个。标题已经定好了方向是从www.domain.com跳到domain.com也就是去掉www。但我要提醒你这不是一个普适答案要看你的 DNS 怎么设的、邮件服务怎么配的、有没有 CDN。如果你决定保留裸域作为主域名那几件事要同步检查。第一裸域有没有配 A 记录或 AAAA 记录解析到服务器上否则用户直接输入domain.com根本打不开跳转也就无从谈起。第二如果企业邮局是走mail.domain.com或者 MX 记录指向裸域不要乱动 DNS先确认裸域解析目前是给网站用的还是邮件用的。第三如果你用了 CDNCDN 上通常也要把裸域和www域同时接入并且把证书覆盖全不然跳转之后出现证书不一致的警告体验比不跳转还差。反过来说如果你以后打算保留www那任务反过来写就可以目标地址换成http://www.domain.com。两种方向我都见过关键是自己心里要有数。2.3 现代站点还要提前想好 HTTPS 与 HSTS 的处理标题给的是http场景但我不建议你只做http这一个层面的重定向。2025 年还说只支持http的站点已经很少了绝大多数情况是http和https并存甚至已经把流量全部切到https。这时候只做“http://www到http://裸域”是不够的更完整的链路是http的任何形态先升级到https再顺便收拢域名。常见的做法是分两步。第一步把https://www.domain.com也 301 到https://domain.com。第二步再让http://www.domain.com和http://domain.com都直接跳到https://domain.com。如果你的服务器已经强制启用 HSTS那浏览器在首次收到Strict-Transport-Security头之后后续都会自动把http请求升级成https重定向逻辑就会少处理很多http流量。但注意HSTS 只在首次成功访问https之后才生效新用户第一次输入http://www.domain.com时仍然需要服务器端给出一个 301。我平时写配置时习惯把这一条链路一口气写完因为你不知道用户是从哪个地址进来的。与其等着后期再补不如一开始就把规则织密。3. Nginx 实操配置最常用的两套写法3.1 双 server 块写法结构最清晰也好维护如果你用的是 Nginx最直观的方案就是在配置文件的server层做两个块一个专门负责接收www域名的请求然后重定向另一个是正常站点配置。下面这套配置我实测了很多次拿过来改改域名就能用。# www 专用跳转 server80 端口 server { listen 80; server_name www.domain.com; return 301 http://domain.com$request_uri; } # 主站 server接收裸域请求 server { listen 80; server_name domain.com; root /var/www/domain.com/public; index index.html index.php; location / { try_files $uri $uri/ /index.php?$query_string; } }这里的关键在return 301 http://domain.com$request_uri;这一行。$request_uri是 Nginx 自带变量代表用户请求的原始路径和查询参数拼上它之后用户访问http://www.domain.com/article?id5会被完整带到http://domain.com/article?id5不会把参数丢掉。这是我见过新手最容易忽略的点很多人只写return 301 http://domain.com;结果用户从一个详情页跳到了首页体验很难说及格。如果有https场景就是再补一个listen 443 ssl;的跳转块同时把证书路径指到正确的server_name。例如server { listen 443 ssl; server_name www.domain.com; ssl_certificate /etc/nginx/ssl/domain.com.pem; ssl_certificate_key /etc/nginx/ssl/domain.com.key; return 301 https://domain.com$request_uri; }两条规则合起来无论用户输入的是http还是https只要域名带www最后都会被统一收拢到https://domain.com非常稳。3.2 单 server 块判断写法适合配置项目已经很多的情况另一种写法是把判断直接放在主站server块内部用if判断$host变量。有些老项目一个配置文件里混着多个 location 规则单独拆一个跳转 server 块反而增加维护成本这时候用单块判断更省事。server { listen 80; server_name domain.com www.domain.com; if ($host www.domain.com) { return 301 http://domain.com$request_uri; } root /var/www/domain.com/public; # 其他 location 配置照旧 }注意Nginx 官方不推荐滥用if文档里明确说过if在location阶段有一些已知的坑。但return是if指令里少数的“安全”用法因为它不会触发内部重定向的复杂逻辑。我自己的习惯是优先用双 server 块只有接手别人很复杂的配置时才会退而求其次用单块。还有一个细节写在if里的时候server_name必须两个域名都写上不然 Nginx 匹配不到www.domain.com就不走这个配置。配置写完先用nginx -t语法检查确认没问题再systemctl reload nginx或者nginx -s reload。这一步我每次都不落下改完不检查直接 reload出了问题很难定位是新配置的锅还是缓存的问题。3.3 验证 Nginx 是否真的生效别只盯着浏览器配置完了怎么验证我不太建议用浏览器直接点因为浏览器有缓存301 这种永久跳转会被记住你第二次再看就看不到真实的响应了。更可靠的方式是用命令行工具curl -I只看响应头。curl -I http://www.domain.com如果配置生效你会看到类似这样的输出HTTP/1.1 301 Moved Permanently Location: http://domain.com/ Server: nginx/1.24.0重点看状态码301和Location字段是否是目标地址。如果 Location 是空白的或者带到了别的地方回到配置文件里查server_name是否匹配、证书是否过期、$request_uri是否拼接完整。每次改完配置顺手敲这一条命令把结果保存下来后面回滚也方便。4. Apache、IIS 与代码层重定向4.1 Apache 环境下用 .htaccess 实现收拢Apache 中最常见的做法是修改站点根目录的.htaccess文件适合没有服务器 sudo 权限、只能用虚拟主机面板的情况。规则写法比 Nginx 直观直接把下面这段放在文件最上方。IfModule mod_rewrite.c RewriteEngine On RewriteCond %{HTTP_HOST} ^www\.domain\.com$ [NC] RewriteRule ^(.*)$ http://domain.com/$1 [L,R301] /IfModule拆开解释一下。RewriteCond判断当前请求的 Host 是不是www.domain.com[NC]表示忽略大小写RewriteRule把(.*)匹配到的路径原样带到目标地址R301强制使用 301 状态码L表示这是最后一条规则处理完就不再看后面的规则了。这里有个细节我踩过坑如果你的站点开启过HTTPS上面这个目标地址最好写成https://domain.com/$1不然好不容易强制 HTTPS 了这条规则又把你打回http那属于自己给自己制造混合内容警告。还有一个隐藏问题需要注意.htaccess只有在 Apache 配置里开启了AllowOverride All时才生效很多生产环境的虚拟主机默认关掉了这个能力。假如你把.htaccess扔上去半天无效先检查httpd.conf或虚拟主机配置里的AllowOverride不要一条规则改十遍。4.2 IIS 下的 URL 重写规则如果你在 Windows 服务器上用 IIS那就用 URL Rewrite 模块。前提是已经安装了IIS URL Rewrite扩展微软官方有免费安装包装完在站点根目录web.config里加一段规则即可。configuration system.webServer rewrite rules rule nameRemove www stopProcessingtrue match url(.*) / conditions add input{HTTP_HOST} pattern^www\.domain\.com$ / /conditions action typeRedirect urlhttp://domain.com/{R:1} redirectTypePermanent / /rule /rules /rewrite /system.webServer /configuration注意action里的redirectTypePermanent这个必须显式写出来很多默认模板会写成Found也就是 302和我们的意图就不一致了。{R:1}对应的是匹配规则里第一组括号捕获的内容也就是说把当前请求的路径原封不动带过去。改完web.config之后无需重启站点IIS 会自动监测到文件变化并加载新规则不过最好还是在浏览器强制刷新一次缓存。4.3 程序代码层做重定向适合没有服务器配置权限的人有些场景是你根本动不了 Nginx 或 Apache只能在应用入口处做跳转。比如你用的是 PHP 程序最常见的做法就是在index.php或者公共入口文件最前面写判断。?php $host $_SERVER[HTTP_HOST] ?? ; if ($host www.domain.com) { $scheme (!empty($_SERVER[HTTPS]) $_SERVER[HTTPS] ! off) ? https : http; $uri $_SERVER[REQUEST_URI] ?? /; header(Location: . $scheme . ://domain.com . $uri, true, 301); exit; }这段逻辑和 Nginx 版完全一致但注意 PHP 的header()函数必须在任何输出之前调用因为响应头一旦发出就无法修改。如果你的框架已经启用了 Session 或者输出了 BOM 头这段判断要放在框架初始化之前也就是最最顶部的入口文件里。否则哪怕有一个空格被输出header()都会报 “Cannot modify header information” 的警告。其他语言同理。Node.js 里可以在中间件里判断req.headers.hostPython 的 Flask 或 Django 可以在before_request钩子里做。思路都是一样的不依赖具体语言关键是三个要素当前 Host 是不是www形态、目标域名是什么、状态码是不是 301。5. 常见问题与排查技巧实录5.1 无限重定向死循环怎么定位配置完站也打不开了浏览器提示“此页面无法正常工作”最常见就是重定向死循环。我遇到过的情况千奇百怪比如只写了www跳裸域但裸域那个 server 块又拼了www两者互相指也遇到过 CDN 那边做了 HTTP 到 HTTPS 的强制跳转源站这边又把 HTTPS 拉回 HTTP两边打架。定位手段很简单用curl -Iv看整个请求链条一次抓出所有 Location 走向curl -Iv http://www.domain.com循环链路会在输出里非常明显比如Location: http://domain.com然后下一轮请求里又出现Location: http://www.domain.com。遇到这种情况把所有跳转规则梳理一遍画一条从用户输入到最终打开的链路确认每个跳转只发生一次别让两个规则彼此“对跳”。另外也要检查浏览器的 HSTS 缓存Chrome 会记住历史上的强制 HTTPS 结果这属于客户端缓存和服务器配置无关。5.2 CDN 场景下的特别注意事项用了 CDN 之后重定向的“主战场”就不一定是源站了CDN 节点自己也可能有回源和跳转逻辑。很多 CDN 控制台里可以做“HTTPS 强制跳转”和“域名级别 301”配置如果你在 CDN 层面已经做了裸域收拢源站反而不要重复做同样的规则否则容易来回跳。实际项目里我见过一个更隐蔽的坑CDN 上同时接入www.domain.com和domain.com两个加速域名但只给其中一个配置了证书导致 HTTP 层面跳转成功后新地址的 HTTPS 证书不匹配。浏览器会先弹证书警告用户一旦手动跳过后续 Cookie、资源请求全部处于不安全状态。所以我的建议是先确认 CDN 的两个域名都已上传或关联了对应证书再做 HTTP 跳转顺序不要反。5.3 验证时出现 404 或者回到了首页有时候状态码是对的但访问带参数的某篇文章却跳到首页这个大概率是重定向时把$request_uri或者(.*)丢了。你回到配置文件看跳转地址检查是否加了动态路径变量。Nginx 版要保证有$request_uriApache 版要保证目标写成http://domain.com/$1而不是http://domain.com/。这也是我前面一再强调的原因这类问题 90% 都是路径没有拼全。如果要排查得更细可以在目标域名下访问一个带参数的长尾路径例如curl -I http://www.domain.com/archives/2025/01?id88看 Location 是否完整保留了后面那段。如果 Location 里干干净净没有任何路径那就去改配置吧。5.4 检测 URL 重定向的小工具和日常验证清单很多时候配置是写对了但你自己心里没底。除了curl -I还可以用在线重定向检测工具输入 URL 后会完整展示每一个跳转步骤和状态码连 301 链路上的响应头都能看到。我一般用它来验证多级跳转链路比如从http://www到https://www再到https://裸域的完整路径工具能帮你一眼看出哪个环节多了或少了状态码。日常维护的话我建议整理一个验证清单每次改完配置就跑一遍第一检查nginx -t或 Apache 配置语法第二用curl -I看 301 状态和 Location第三访问一个带参数的深层链接确认路径不丢第四在隐身模式或换一个浏览器实测一次。这四步都过了再放给用户访问也不迟。结束前的一点经验补充这类域名收拢需求我前前后后处理过很多次每次感觉都不太一样。一开始我觉得就是一行return 301的事后来发现真正花时间的是处理各种边界情况HSTS 头、CDN 证书、Cookie 域、参数拼接任何一个环节瘸腿都会导致用户访问异常。所以我个人的真实建议是不要把这篇文章里的某一段配置当成万能药而是要结合你自己网站的域名部署方式先把“用户从哪个地址进来、需要去哪、中间经过谁”这条链路画清楚再动手写配置。最后再分享一个小技巧改完重定向之后最好顺手检查一下你其他页面里有没有硬编码的旧域名链接比如 HTML 内部跳转、JS 跳转、邮箱模板里的链接不然服务器层面收拢了前端代码又把用户拽回旧地址每次都多一次不必要的跳转既有性能开销也影响体验。我每次迁移域名都会全站 grep 一次旧域名这个习惯帮我提前发现了不少隐蔽问题。希望这篇也能让你少踩几次坑。
返回列表