HTTPS 没强制——站点同时服务 http 和 https

你的站点 http 和 https 都能开。Google 把两个版本都收录,安全评分下降。

http://yourdomain.com 站点能打开——没跳 HTTPS。Search Console 把每个 URL 的 http 和 https 都单独列。Chrome 在 http 请求显示 “Not Secure”。不强制 HTTPS 的话,对 Google 来说你有一个重复站、安全评分被降,而且现代浏览器越来越歧视未加密流量。修法是平台/CDN 层加 http → https 的 301 重定向,外加 HSTS 头把浏览器锁定到 HTTPS。

常见原因

按命中率从高到低。

1. 托管平台不自动强制

某些主机(旧 Firebase 配置、自建 Nginx、某些共享主机方案)不自动 http → https。要手动开。

怎么判断

curl -sI "http://yourdomain.com" | head -3

返回 200 = 没重定向。应该是 301 带 Location: https://...

2. Cloudflare 用了 “Flexible SSL” 模式

“Flexible” 在 Cloudflare 边缘终结 SSL 但跟源站用 HTTP 通讯。访客看到 HTTPS 但源站还是 HTTP——不安全,且会破坏源站的 HTTPS-only 设置。

怎么判断:Cloudflare → SSL/TLS → Overview → 模式如果是 “Flexible”,改成 “Full” 或 “Full (strict)“。

3. 没设 HSTS

哪怕有重定向,浏览器首次访问还是先试 HTTP。HSTS 告诉浏览器”这个域名永远 HTTPS”,后续访问跳过 http 那一跳。

怎么判断curl -sI https://yourdomain.com | grep -i strict-transport-security。为空 = 没 HSTS。

4. 重定向写在 app 代码、不在平台层

你在 app 代码(Express、Astro middleware)加了重定向,但它在 app 收到请求后才触发。某些 HTTP 请求根本到不了那里(CDN 拦了、静态资源路径)。

怎么判断:某些 URL 重定向、某些不。不重定向的通常是 app 代码运行之前服务的静态资源。

5. 自建 CDN / 代理直接服务 http

源站前的 CDN 配成 http 和 https 都服务。没显式规则的话 http 就保持 http。

怎么判断:绕过 CDN(curl 源站)看源站会不会重定向。源站重定向但 CDN 不重定向 = CDN 配置问题。

6. 混合内容阻碍 HSTS

<img src="http://..."> 等混合内容会让浏览器不信 HSTS。不算严格”HTTPS 未强制”,但相关症状。

怎么判断:DevTools 控制台找 “Mixed Content” 警告。

最短修复路径

第 1 步:平台层启用 force-HTTPS

Vercel — 默认开。Domain 设置里确认。

Netlify — Site settings → Domain management → HTTPS → “Force HTTPS”。勾上。

Firebasefirebase.json

{
  "hosting": {
    "redirects": [
      {
        "source": "**",
        "destination": "https://yourdomain.com/:1",
        "type": 301
      }
    ]
  }
}

或用 Firebase 内建(新项目自动强制)。

Cloudflare — SSL/TLS → Edge Certificates → “Always Use HTTPS” → 开。

第 2 步:加 HSTS header

Vercelvercel.json

{
  "headers": [{
    "source": "/(.*)",
    "headers": [
      { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" }
    ]
  }]
}

Netlify_headers

/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

先用短 max-age(300 = 5 分钟)测,稳定后再升到 63072000(2 年)。

第 3 步:Cloudflare 用 Full (strict)

SSL/TLS → Overview → 选 “Full (strict)“。Cloudflare 到源站加密并验证源站证书。

源站证书有问题先修源站(用平台证书或 Cloudflare Origin Certificate)。

第 4 步:curl 验证

curl -sI "http://yourdomain.com" | head -5

应返回:

HTTP/1.1 301 Moved Permanently
Location: https://yourdomain.com/

HTTPS:

curl -sI "https://yourdomain.com" | grep -i strict-transport-security

应显示 HSTS 头。

第 5 步:Search Console 提交 https URL

Search Console → URL Inspection → 对重要 URL 请求收录 https 版。Google 会把 http 合并到 https 作为 canonical。

第 6 步:考虑 HSTS preload

HSTS 平稳运行几个月后,把域名提交到 HSTS preload 列表。Preload 后,所有浏览器从首次访问就强制 HTTPS,连联系你服务器之前就强制。注意:从 preload 列表移除要几周。完全确定 HTTPS 永远工作再 preload。

哪些情况可能不是你操作错了

老配置可能需要手动 toggle 强制 HTTPS。新平台(Vercel、Netlify、Cloudflare Pages)默认强制。

容易误判的情况

只设 <link rel="canonical" href="https://..."> 不能阻止 Google 收录 http 版。真正让 http 退索引的是 301 重定向。

预防建议

  • 第一天就在平台层强制 HTTPS。不要不开 HTTPS 就上线。
  • HTTPS 稳定 30+ 天后加 2 年 max-age 的 HSTS。
  • Cloudflare 用 “Full (strict)” SSL,永远别用 “Flexible”。
  • 任何基础设施变更后 curl -sI http://yourdomain.com 验证仍是 301。
  • 审计混合内容警告(<img src="http://...">)并修。

FAQ

  • HSTS 会阻止回滚吗? 会——这是目的。浏览器在 max-age 秒内不会试 http。升 max-age 前先测稳定。要退出 HSTS,设 max-age=0;preload 列表移除要几周。
  • 需要付费 SSL 吗? 不需要——绝大多数站点 Let’s Encrypt 或平台提供的证书都够用。

相关阅读

标签: #域名 #DNS #SSL #排查 #强制 HTTPS