网站还在用 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:有效期一年(单位秒),浏览器在这段时间内强制走 HTTPSincludeSubDomains:所有子域名也一起锁死preload:声明愿意进入浏览器预加载列表(下面讲)
加 always 是为了让错误页面(比如 404)也带上这个头。
⚠️ 上线 HSTS 前先确认全站子域名都支持 HTTPS。
一旦带了
includeSubDomains,某个只有 HTTP 的子域名就彻底打不开了,而且浏览器缓存期内你改都改不掉。建议先把max-age设小一点(比如 300 秒)灰度观察,没问题再调到一年。
方法四:HSTS preload 预加载列表
HSTS 还有个更狠的玩法。它依然有"第一次"问题——浏览器从没访问过这个站,就不知道该锁 HTTPS。preload 解决的就是这个。
把域名提交到 hstspreload.org,通过审核后,你的域名会被内置进 Chrome、Firefox、Safari、Edge 等浏览器的源码里。这样任何人、任何浏览器、从第一次访问开始就强制 HTTPS,一次明文请求都不会发。
提交的硬性条件:
- 响应头里
max-age至少31536000(一年) - 必须带
includeSubDomains - 必须带
preload - 根域名要有有效证书,且 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 Permanently,Location是不是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/