目录

网站还在用 HTTP?怎么把它强制跳转到 HTTPS

证书装好了,网址前面的小锁也出来了,结果有人直接敲 http:// 还是能进站。

这事儿挺常见。

光装证书不等于全站 HTTPS,你得再加一步:把所有 HTTP 请求强制转到 HTTPS 上去。

下面按服务端、协议头、前端三个层面讲,配置都给全。

为什么装了证书还得做强制跳转

装 SSL 证书只是让服务器用 HTTPS,并不代表用户必须走 HTTPS。

浏览器历史记录、外链、手输地址,很多还是 http:// 开头。

如果不做跳转,这些流量就在明文里裸奔,中间人能随便看、随便改。

还有个现实问题:搜索引擎会把 http://https:// 当成两个不同的页面。不跳转,权重就分散了。所以强制跳转既是安全需求,也是 SEO 需求。

方法一:Nginx 用 301 重定向

最常用的方案。

Nginx 里单独写一个监听 80 端口的 server 块,把所有请求 301 到 HTTPS:

server {
    listen 80;
    server_name example.com www.example.com;
    # 301 永久重定向,把协议换成 https,路径和参数原样带过去
    return 301 https://$host$request_uri;
}

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

    ssl_certificate     /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # 你的正常站点配置...
}

这里有两个坑我踩过。

一个是别用 302,要用 301

301 是永久重定向,搜索引擎会把权重转过去;

302 是临时的,权重不传,还可能被反复请求。

另一个是别写 rewrite ^ https://...,直接用 return 301 更快,不走正则匹配。

改完记得 nginx -t 测一下语法,再 nginx -s reload

方法二:Apache 用 .htaccess

如果你用的是 Apache,且开启了 mod_rewrite,在网站根目录的 .htaccess 里加几行就行:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

R=301 同样是永久跳转,L 表示匹配后停止往下走。

有一种情况要注意:站点挂在 Cloudflare 这类 CDN 或反向代理后面时,%{HTTPS} 可能一直是 off,因为代理到源站走的是 HTTP,会导致无限跳转。这时候改成判断 X-Forwarded-Proto 头:

RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

我去年帮人排查过一次"打不开网站",就是没注意这个,源站和 CDN 互相跳,浏览器直接报 ERR_TOO_MANY_REDIRECTS

方法三:HSTS,让浏览器自己走 HTTPS

301 跳转有个小破绽:用户第一次访问还是先发了一个明文的 HTTP 请求,然后才被跳走。

这一下握手的间隙,理论上仍有被劫持的风险。

HSTS(HTTP Strict Transport Security)就是来补这个洞的。

服务器返回一个响应头,告诉浏览器:“以后这个域名你别用 HTTP 了,直接上 HTTPS”。

浏览器记住之后,连第一次的明文请求都省了,地址栏敲 http:// 也会被浏览器内部直接改写。

Nginx 在 443 的 server 块里加一行:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

参数拆开看:

  • max-age=31536000:有效期一年(单位秒),浏览器在这段时间内强制走 HTTPS
  • includeSubDomains:所有子域名也一起锁死
  • preload:声明愿意进入浏览器预加载列表(下面讲)

always 是为了让错误页面(比如 404)也带上这个头。

⚠️ 上线 HSTS 前先确认全站子域名都支持 HTTPS。

一旦带了 includeSubDomains,某个只有 HTTP 的子域名就彻底打不开了,而且浏览器缓存期内你改都改不掉。建议先把 max-age 设小一点(比如 300 秒)灰度观察,没问题再调到一年。

方法四:HSTS preload 预加载列表

HSTS 还有个更狠的玩法。它依然有"第一次"问题——浏览器从没访问过这个站,就不知道该锁 HTTPS。preload 解决的就是这个。

把域名提交到 hstspreload.org,通过审核后,你的域名会被内置进 Chrome、Firefox、Safari、Edge 等浏览器的源码里。这样任何人、任何浏览器、从第一次访问开始就强制 HTTPS,一次明文请求都不会发。

提交的硬性条件:

  1. 响应头里 max-age 至少 31536000(一年)
  2. 必须带 includeSubDomains
  3. 必须带 preload
  4. 根域名要有有效证书,且 HTTP 已 301 跳到 HTTPS

提交容易,撤销极难。想从 preload 列表里移除,得重新提交申请,浏览器再随版本慢慢删,周期以月计。所以这步只对长期稳定、确定全站 HTTPS 的域名做。

方法五:前端 CSP 处理混合内容

服务端跳转解决了页面入口,但页面里如果还引用了 http:// 的图片、脚本、样式,浏览器会报"混合内容"(Mixed Content)警告,小锁可能就变成黄色感叹号了。

最省事的办法是加一行 CSP 指令,让浏览器自动把页面内所有 HTTP 资源请求升级成 HTTPS:

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

或者在响应头里下发:

add_header Content-Security-Policy "upgrade-insecure-requests" always;

这招对老站点改造特别好用,不用一个个去翻代码里的 http:// 链接。不过它只是"升级请求",根上还是建议把资源链接改成 https:// 或协议相对的 // 写法。

几种方法怎么搭配

它们不是二选一,而是层层叠加:

层面 方法 作用
服务端入口 Nginx/Apache 301 跳转 兜底,把所有 HTTP 请求转走
协议头 HSTS 让回访用户跳过明文请求
浏览器内置 HSTS preload 让首次访问也强制 HTTPS
页面资源 CSP upgrade-insecure-requests 消除混合内容

实战推荐:先做好 301 跳转(必做),再加 HSTS(强烈建议),混合内容多就上 CSP,域名稳定后考虑 preload。

怎么验证配好了没

改完别急着收工,验证一下:

  • 命令行跑 curl -I http://example.com,看返回是不是 301 Moved PermanentlyLocation 是不是 https://...
  • 再跑 curl -I https://example.com,看响应头里有没有 Strict-Transport-Security
  • 浏览器开发者工具的 Network 面板,看首个请求的状态码和跳转链
  • 想查 HSTS 缓存状态,Chrome 地址栏输入 chrome://net-internals/#hsts,能查询和删除单个域名的记录(调试时很有用)

常见问题

HTTP 强制跳转 HTTPS 应该用 301 还是 302?

用 301。

301 是永久重定向,搜索引擎会把旧地址的权重转移到 HTTPS 地址,浏览器也会缓存跳转结果,下次直接走 HTTPS。

302 是临时重定向,权重不传递,每次还得重新请求一遍,对 SEO 和性能都不利。

配了 HSTS 之后想关掉 HTTPS 还能改回来吗?

很麻烦。

HSTS 的 max-age 缓存在用户浏览器里,你把服务端的头删了,已经访问过的用户在缓存到期前仍会被强制走 HTTPS。

如果还提交了 preload,浏览器内置的记录更是没法即时撤销。

所以上线前一定先小步灰度,别一上来就设一年。

为什么我配了跳转却出现"重定向次数过多"?

大概率是网站挂在 CDN 或反向代理后面。

代理回源站走的是 HTTP,源站判断到 HTTP 又往 HTTPS 跳,两边来回踢皮球就死循环了。

解决办法是改用 X-Forwarded-Proto 请求头来判断真实协议,而不是直接看 %{HTTPS}

只装了证书不做跳转有什么风险?

用户仍然能通过 http:// 明文访问你的站点,数据可被监听和篡改;

搜索引擎把 HTTP 和 HTTPS 当两个站,权重分散;

浏览器对纯 HTTP 站点会标"不安全",影响信任度。

装证书只是第一步,强制跳转才算真正完成全站 HTTPS。

upgrade-insecure-requests 能替代修改 http 链接吗?

能救急但不能根治。

它让浏览器自动把页面内的 HTTP 资源请求升级成 HTTPS,适合老站点快速改造。

但前提是那些资源本身得支持 HTTPS,否则升级后照样加载失败。

长期还是建议把代码里的链接直接改成 HTTPS 或协议相对写法。

写在最后

强制 HTTPS 这事,说复杂不复杂,核心就是"服务端兜底跳转 + HSTS 锁浏览器"。

大多数站做到 301 加 HSTS 就足够了,preload 和 CSP 看情况上。

唯一要反复提醒的是 HSTS 那个一年缓存,谨慎设值,别给自己埋雷。

如果你在配置过程中遇到死循环、混合内容或者证书链的问题,欢迎在评论区贴出报错,我们一起看看~~~

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/force-website-redirect-to-https/

备用原文链接: https://blog.fiveyoboy.com/articles/force-website-redirect-to-https/