前阵子帮朋友迁移一个老项目SSL证书在控制台点了几下就签下来了443端口也通HTTPS页面访问一切正常。但我正准备收工朋友来了一句“直接输网址怎么还是跳到HTTP地址栏还提示不安全”这问题在Apache上太典型了——证书配好了80端口的明文流量却没处理全站强制HTTPS只做了一半。要补上这一半核心就是一件事把进入80端口的请求全部重定向到443端口。今天这篇就把Apache里两种最主流的跳转方案一次讲透适合刚入门的新手也适合想搞清原理后自己裁剪的老手。1. 先搞清楚HTTP到HTTPS的跳转到底要解决什么问题1.1 为什么证书配好了还要单独再配一层跳转很多人在Apache上部署完证书后有个误区以为443能正常访问就等于全站HTTPS了。但实际上HTTP和HTTPS在Apache里是两套独立的服务分别监听80和443端口。你给443端口配上证书只是让“带锁的门”可以通行而80端口那扇“不带锁的门”依然敞开着。这时候用户在浏览器地址栏输入不带协议的域名浏览器默认会走80端口拿到的还是明文HTTP响应地址栏自然还是“不安全”状态。更麻烦的是搜索引擎会把HTTP和HTTPS当成两个站点来抓取权重分散、链接重复对SEO很不友好。所以证书只解决了“能不能用HTTPS”跳转解决的是“所有人必须用HTTPS”。这两件事缺一不可。1.2 301和302的选择线上环境必须用301跳转方案里绕不开两个状态码301和302。它们的作用差别很关键。301 Moved Permanently永久重定向告诉浏览器和搜索引擎“这个地址已经永久搬家了以后都去新地址”。302 Found临时重定向告诉浏览器“这次先借道去新地址但老地址以后还要用”。对于HTTP跳HTTPS这种场景老地址HTTP站点是彻底废弃的所以必须用301。如果你在测试阶段还没确认线上要不要保留HTTP服务可以用302先顶着等确认后再切成301。这里有个容易踩的坑301会被浏览器缓存。你在测试阶段配了301浏览器记住了一次之后哪怕你在服务器上把配置改掉浏览器还是会直接跳到HTTPS甚至你回退成302都没用。所以我自己的习惯是测试阶段一律用302确认没问题了再切成301发布。避免在调试过程中被缓存干扰判断。1.3 动手之前先确认三件事配置跳转之前先检查以下条件避免配完发现跳过去的是个打不开的HTTPS站点443端口确实能正常访问HTTPS证书链完整、没有过期是前提。先访问一下https://你的域名确认页面正常再开始配跳转。确认对应的模块已加载用httpd -M看输出。方案一用的rewrite_module在部分系统默认不启用必须确认它出现在列表里方案二用的alias_module基本是默认加载的。确认80和443端口都在监听netstat -tlnp | grep httpd或者ss -tlnp | grep httpd都能看到。有些精简配置只监听了443那就不存在“HTTP跳HTTPS”的问题了但也说明你还没把HTTP服务完整跑起来。这三项确认完就可以开始配置了。下面两种方案按需选一种落地方案。2. 方案一mod_rewrite重写规则精细化控制首选2.1 最简配置四行规则让全站强制HTTPSmod_rewrite是Apache里功能最强大的URL处理模块也是被问得最多的一种方式。标准的全站跳转配置长这样VirtualHost *:80 ServerName example.com RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R301] /VirtualHost逐行拆开解释一下RewriteEngine On开启当前作用域的重写引擎不写这个下面的规则一概不生效。RewriteCond %{HTTPS} off这是一个条件判断检查环境变量%{HTTPS}是否等于off。Apache在收到HTTPS请求时这个变量会被置为on收到普通HTTP请求时则是off。所以这句话的意思是“只有当请求是HTTP时才执行后面的规则”。RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R301]把匹配到^(.*)$的请求全部重定向。^(.*)$是正则表达式匹配任意路径%{HTTP_HOST}是用户原始请求里的域名保留它不会跳错域名%{REQUEST_URI}是原始请求的路径和查询参数保留它能做到路径不丢。后面的L表示这是最后一条规则处理完就停止R301表示以301状态码返回重定向。这个配置放在VirtualHost *:80里对当前站点也就是80端口生效。放进配置文件后记得apachectl -t测一遍语法然后systemctl reload httpd或systemctl reload apache2取决于系统发行版让配置生效。2.2 多域名与www场景把规则写严避免跳飞只用上面的最简配置在多域名或者www/非www混用的情况下会碰到一些小问题。比如你的站点同时绑了example.com和www.example.com你想让所有流量都汇聚到https://www.example.com那就要在Rewrite规则里显式写出目标域名而不是用%{HTTP_HOST}。RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://www.example.com/$1 [L,R301] # 非www的HTTPS请求也统一跳到www RewriteCond %{HTTP_HOST} ^example\.com$ [NC] RewriteRule ^(.*)$ https://www.example.com/$1 [L,R301]这里有个小细节第二条规则我没写RewriteCond %{HTTPS} off而是通过RewriteCond %{HTTP_HOST} ^example\.com$来匹配域名。因为既可以处理HTTP的example.com也可以处理已经走HTTPS但没带www的请求把它们都归拢到带www的最终地址。你如果计划保留“不带www”作为主地址把规则里的example.com和www.example.com对调就可以了。现实项目里域名规则经常很乱我建议你把要跳转的域名整理成一个清单一个一个检查。之前我给某个项目做迁移发现一个旧域名被第三方业务引用直接跳到新域名后路径全丢了后来就是靠RewriteRule里显式拼接/$1才保住路径这个经验很值得收藏。2.3 加白名单让部分路径继续走HTTP有些场景下全站强制HTTPS反而不合适。比如运维健康检查接口、部分回调通知、或者Lets Encrypt的证书续期验证目录。如果你在HTTP端一刀切全部301这些访问会被强行带到HTTPS可能导致验证失败或者监控误报。解决办法是在RewriteCondition里加“例外”条件把希望保持HTTP的路径排除掉RewriteEngine On RewriteCond %{HTTPS} off RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ RewriteCond %{REQUEST_URI} !^/healthz RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R301]注意!是取反的意思!^/\.well-known/acme-challenge/表示“请求路径不是以这个目录开头”。加上这条以后访问http://example.com/.well-known/acme-challenge/xxx时第二条RewriteCond匹配成功第二条条件成立就表示应该排除整体条件不通过规则不执行请求就能被保留在HTTP端口。而其余路径照样被重定向到HTTPS。这种精细控制是mod_rewrite的最大优势也是我偏好这个方案的重要原因。2.4 反向代理场景%{HTTPS} off为什么失效不少线上系统是Apache后面再挂一个Nginx或负载均衡器SSL证书终止在更前面的那一层Apache本身拿到的是明文HTTP请求。这时候你配置RewriteCond %{HTTPS} off会发现一个诡异现象明明用户浏览器访问的是HTTPSApache这边却一直认为这是HTTP然后不停把用户重定向回HTTPS形成一个跳转死循环。根源在于SSL协议在Nginx那层就解开了Apache接收到的连接确实就是普通的HTTP%{HTTPS}变量自然为off。这种情况不能简单判断端口需要依赖转发进来的头部信息。如果前端节点在转发请求时携带了X-Forwarded-Proto头那么Apache侧可以改成这样RewriteEngine On RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R301]%{HTTP:X-Forwarded-Proto}是读取请求头X-Forwarded-Proto的值。当浏览器访问HTTPS时前端代理会把X-Forwarded-Proto: https传给Apache!https判断不成立不会跳转当浏览器访问HTTP时代理传过来的值通常是http条件成立正常跳转。注意这里有个安全前提你必须保证只有可信的前端代理能访问Apache的80端口否则别人伪造一个X-Forwarded-Proto: https就能绕过跳转规则。有条件的话让Apache只监听内网IP或者加上访问控制更稳。3. 方案二mod_alias的Redirect指令简单场景一把梭3.1 配置其实只有一行第二种方案没有复杂的正则条件和变量只需要在VirtualHost里写一行VirtualHost *:80 ServerName example.com ServerAlias www.example.com Redirect permanent / https://example.com/ /VirtualHostRedirect指令是mod_alias模块提供的语法是Redirect [状态码] 路径前缀 目标URL第一个参数permanent表示301也可以直接写301或者省略不写让Apache默认返回302。线上建议明确写permanent。第二个参数/是匹配的路径前缀在这里匹配该VirtualHost下的所有路径。第三个参数https://example.com/是重定向的目标地址。这个方案最大的优点是简单清晰配置一眼就能看懂不用记正则更不用记{HTTPS}变量到底是什么状态。3.2 Redirect的路径拼接逻辑为什么会自动带上原路径很多第一次用Redirect permanent / https://example.com/的人都会有疑问我访问http://example.com/blog/post?id1跳转过去会不会变成https://example.com/而不是https://example.com/blog/post?id1实际上Redirect在做前缀匹配时会把原始路径中“匹配到的部分之后”的内容自动追加到目标URL后面。也就是说/匹配了整个路径那么整个路径和查询参数都会被原样保留。访问上面的链接最终会跳到https://example.com/blog/post?id1一切保持不变。如果只想让某个子路径跳转可以单独配置VirtualHost *:80 ServerName example.com Redirect permanent /old https://example.com/new /VirtualHost当用户访问http://example.com/old/page时会跳到https://example.com/new/page。前缀/old匹配后剩余部分/page自动被追加到https://example.com/new后面。这一条用得很频特别是网站改版、目录结构调整的时候比Rewrite规则好懂得多。3.3 用RedirectMatch做正则匹配的进阶用法Redirect本身不支持正则但它的表亲RedirectMatch可以。如果遇到类似http://example.com/article/12345想精确对应到https://example.com/article/12345的场景可以这么写VirtualHost *:80 ServerName example.com RedirectMatch permanent ^/(.*)$ https://example.com/$1 /VirtualHostRedirectMatch会匹配整个请求路径并允许你通过正则分组$1来引用捕获到的内容灵活度比Redirect高出不少。日常使用频率虽然不算高但它在处理“路径参数需要重写”的迁移场景里非常舒服。3.4 Redirect的局限没有条件判断能力我也不想把Redirect方案吹上天。它的核心局限在于只能做前缀匹配无法根据请求头、域名、端口等条件做分支。比如你绑定了两个域名只希望old.example.com跳转到新域名www.example.com却要正常访问用Redirect就很难优雅实现——你只能在两个VirtualHost里分别配而不是在同一个规则里通过条件区分。再比如你想排除健康检查路径Redirect做不到它没有跳过机制一旦/前缀匹配上所有路径都会被带走。这种场景下你还是得回到mod_rewrite方案。所以Redirect方案适合的目标是单域名、无特殊情况、配置完就不想再动的“佛系站点”。一旦你的业务开始有“如果”“除了”“但是”这类词就该考虑方案一了。4. 两种方案怎么选实用视角的综合对比4.1 一张表看清能力边界我把两个方案的差异整理成一个表方便你快速对照对比项mod_rewrite方案一mod_alias Redirect方案二配置复杂度需要理解正则和RewriteCond条件一行指令容易上手全站跳转能力强条件齐全强路径自动拼接多域名/多条件分支支持逻辑集中在一个VirtualHost支持有限通常需要拆多个VirtualHost路径排除/白名单支持用RewriteCond取反即可不支持除非改动结构反代场景判断支持读取X-Forwarded-Proto无法直接判断需依赖外部机制维护成本略高规则多了容易被正则绕晕低别人接手一看就懂性能规则少时差别可忽略与mod_rewrite无本质差别4.2 我实际使用中的选型思路我自己给项目搭跳转时一般按这个思路来能用Redirect就先用Redirect。特别是个人博客、企业官网、只有一两个域名的简单站点一行配置不用动脑子半年后回来看还能秒懂。这类站点把时间花在正则调试上没意义。一旦出现多域名收敛、路径排除、反代链路直接切Rewrite。比如某个站既要处理http://example.com跳转又要让http://m.example.com也跳到同一个HTTPS地址还要排除Lets Encrypt验证目录我绝不会用三个VirtualHost拼凑直接在Rewrite里把条件写清楚逻辑集中、好排查。无论选哪个都在测试环境先验证一遍再上线。我见过太多直接在线上主环境加上301配置结果因为证书链没排对跳过去直接白屏的情况。4.3 优先级陷阱Rewrite和Redirect混用时的执行顺序有的配置里既有RewriteEngine规则也有Redirect指令这时候执行顺序不是“按配置文件里写的先后排”而是有固定机制的mod_rewrite先执行mod_alias的Redirect后执行。这意味着如果你想通过Rewrite先做某些地址改写再交给Redirect做跳转最终结果可能不是你预期的。比如你写了Redirect permanent / https://example.com/ RewriteRule ^/old/(.*)$ /new/$1 [PT]看起来是先重写 /old 路径但实际上Redirect会先处理/前缀把整个请求直接301到HTTPSRewrite规则根本不参与后续处理。我建议你在一个VirtualHost里只使用一种跳转机制来回混用会让排查成本大幅上升。5. 部署时容易踩的坑与验证方法5.1 坑一301缓存配置改完浏览器还在跳旧地址这是我在方案里反复提醒过的点。浏览器会对301响应做缓存命中的时候甚至不会发请求到服务器直接使用历史缓存的跳转地址。所以你在线上改跳转规则时经常会出现“服务端明明改了Chrome还是跳到老地址”的情况。应对手段测试阶段用302正式切换才用301。调试时用curl -I看响应头不要总依赖浏览器。必要时用无痕窗口排除缓存干扰。5.2 坑二反代后面Rewrite条件失效跳转成死循环前面2.4已经讲了原理证书终止在Nginx层时Apache拿到的都是HTTP连接%{HTTPS} off永远成立。如果没处理会出现“用户访问HTTPS—Apache返回301回到HTTPS—浏览器再请求—Apache再返回301”的无限循环甚至造成服务端日志刷屏。我在一个项目上遇到过类似问题当时整了半天才发现是反代后面跳转发疯。改成X-Forwarded-Proto判断后立刻恢复正常。如果你用的是云负载均衡而不是自建Nginx一样要看厂商文档确认转发时会带哪些头不同的产品带的头不一样。5.3 坑三证书不完整导致443打不开配完跳转反而网站白屏跳转配置本身没毛病但证书链缺少中间证书或者服务器没监听443导致用户被301导到443端口后浏览器直接报“无法建立安全连接”。这种情况在配置跳转前就应该排查掉curl -I https://example.com如果返回正常状态码比如200或者你业务自己的状态码说明HTTPS侧没问题再回头配置跳转。如果curl报SSL证书错误先解决证书问题跳转排到后面。5.4 验证手段curl -I和无痕窗口配置完成并reload之后我习惯用下面几条命令验证# 查看HTTP请求是否被301 curl -I http://example.com # 查看跳转目标是否可达 curl -I https://example.com前者应该出现类似这样的返回HTTP/1.1 301 Moved Permanently Location: https://example.com/如果返回200说明80端口还在正常响应没有触发跳转检查RewriteCond条件或VirtualHost是否匹配正确。后者的意义是确认443端口可用、证书有效。5.5 顺手加上HSTS让浏览器强制走HTTPS跳转配置只是服务端的一种“告知”行为浏览器第一次访问后下次依然可能尝试HTTP。想彻底一点可以再给HTTPS响应加上HSTSHTTP Strict Transport Security响应头让浏览器在一段时间内对当前域名强制走HTTPSVirtualHost *:443 ServerName example.com Header always set Strict-Transport-Security max-age31536000; includeSubDomains /VirtualHost这条配置需要mod_headers模块支持。max-age31536000表示一年内浏览器对这个域名只走HTTPSincludeSubDomains表示子域名也生效。HSTS会让跳转的“次数”减少很多因为浏览器内部就把HTTP访问改写成HTTPS了。注意HSTS加不加、加多大取决于你的业务是否确定长期只用HTTPS。如果未来要回退到HTTP浏览器会严格拒绝这是很多人容易忽略的“隐形锁”。实际做这种操作时我还有个习惯把配置改动前后各备份一次再用apachectl -t检查语法再reload。一次配置操作花不了两分钟但能省掉很多不必要的返工和线上事故。等你哪天接手一个半年没动过的老配置你就知道备份文件有多救命了。
阅读完成 · 觉得有帮助?