把老 PRO 工具搬上 HTTPS: 我给 8000 端口装上正式证书的 6 小时
上周三下午 3 点, 我在星巴克打开手机想用 PRO 工具查一下上半场路单, Safari 弹了个大红色警告: "此网站不支持安全连接"。电脑上好好的, 一上手机就废。当时第一反应是 3G/4G 网络问题, 换了 WiFi 一样。
回家打开 Chrome 复现: 自签证书在桌面是 "可以继续访问" 的小黄条, 在 iOS Safari 直接是 "不允许继续"。Android Chrome 更狠, 根本没 "继续" 按钮。
这篇文章记录我从发现 → 查问题 → 装 ZeroSSL → 部署 → 踩坑 → 修好的完整 6 小时, 顺便解释为什么这件事对任何跑本地/自部署工具的朋友都很重要。
症状: 80% 的移动浏览器拒绝自签证书
先说结论, 免得你看完才发现不是你的问题:
- ✅ 桌面 Chrome / Edge / Firefox: 弹 "您的连接不是私密连接" → 点 "高级" → "继续前往" → 能用
- ❌ iOS Safari (无痕模式): 直接挡死, 普通模式能进但 5 秒延迟 + 红色全屏警告
- ❌ Android Chrome 90+: 没有任何 "继续" 选项, 直接
ERR_CERT_AUTHORITY_INVALID - ❌ 微信内置 / QQ / 微博 浏览器: 100% 静默拦截, 页面转圈后白屏
我做了个粗略统计: 给身边 12 个朋友发了 PRO 工具链接, 桌面 12 个里 11 个能用, 移动端 12 个里 只有 2 个 (都是老 Android + 旧 Chrome) 能用。等于把 80% 的潜在用户卡在门外。
作为对比, 我们家的 PHD 工具 也是同样情况, 改完之后手机能直接进, 后面会有对比图。
决策: 不买商业证书, 选 ZeroSSL 免费版
我的需求很简单:
- 必须是受信任 CA 签发的证书 (不是自签)
- 有效期至少 3 个月以上 (我懒得每月续)
- 支持单域名, 必须是
www.baccpc.com(我的老 PRO 跑在 8000 端口的子域) - 不要钱
扫了一圈:
| CA | 免费 | 单域 | 有效期 | DV 验证方式 |
|---|---|---|---|---|
| Let's Encrypt | ✅ | ✅ | 90 天 | HTTP / DNS |
| ZeroSSL | ✅ (3 个免费) | ✅ | 90 天 | DNS CNAME / HTTP |
| Cloudflare | ✅ (需 CF 代理) | ✅ | 15 年 | 自动 |
| Sectigo / DigiCert 商业 | ❌ ($50+/年) | ✅ | 1 年 | DNS / 文件 |
最终选 ZeroSSL。理由: DNS CNAME 验证不用碰服务器文件 (Let's Encrypt HTTP 验证要装 acme.sh 或者暴露 80 端口, 我机器 80 已经被 IIS 占了)。
(后话: 90 天后续期也是同流程, 我打算下周单独写一篇续期 SOP。)
实施: 4 步装好证书
总耗时 6 小时, 其中 4 小时是踩坑 (后面讲), 实际干活就 30 分钟。流程:
Step 1: ZeroSSL 申请证书 (10 分钟)
去 zerossl.com 注册 → New Certificate → 填 www.baccpc.com → 选 90 天免费 → 它会给你一个 CNAME 记录让你加到 DNS:
主机: _5CA856F20F2D109CF6B26F0927B1A43C.baccpc.com
值: 9f7b11cfa5af85172870e15936857fb9.4a282cda8c9d892096197e0833bf72a0.ab12883837f650a.comodoca.com
去 GoDaddy DNS 加一条 CNAME, 等 5-10 分钟验证通过。
Step 2: 下载证书 (1 分钟)
ZeroSSL 控制台下载 certificate.crt + ca_bundle.crt (中间链) + private.key。把 leaf + 中间链合并成一个 cert.pem。
坑 #1: 直接
cat leaf.crt ca_bundle.crt拼出来, PowerShell 5.1 默认会加 CRLF 换行符, Python 3.8 解析时报KEY_VALUES_MISMATCH。最后用 Pythoncryptography库按 LF 重新生成才解决。
Step 3: Flask 加 ssl_context (5 分钟)
老 PRO 是 Flask 应用, app.run() 改成:
from werkzeug.serving import make_server
import threading
cert = "cert.pem"
key = "key.pem"
# HTTPS 8000
https_srv = make_server('0.0.0.0', 8000, app,
ssl_context=(cert, key),
threaded=True)
t = threading.Thread(target=https_srv.serve_forever, daemon=True)
t.start()
# 主线程等
https_srv.serve_forever()
一行加 ssl_context 就完事。Flask 1.0+ 原生支持, 不需要 gunicorn / nginx。
Step 4: 验证 (10 分钟)
浏览器开 https://www.baccpc.com:8000/, 应该看到:
- 地址栏绿色锁标
- 证书信息显示 "ZeroSSL RSA DV SSL CA 2" 颁发
- 无 "您的连接不是私密连接" 警告
手机 4G / 5G / WiFi 各测一次, 都能进就 OK。
踩坑清单 (省你 4 小时)
这是最值钱的部分。我掉的坑:
- PowerShell Get-Content 拼证书会污染 CRLF → 改用 Python cryptography 库, 严格 LF 换行
- ZeroSSL 免费版只支持单域名 → 我一开始填了 baccpc.com + www.baccpc.com, 实际只签了第一个 → 重新申请只填 www
- GoDaddy DNS CNAME 首尾空格 → 复制粘贴时带了空格, 验证一直失败 → 用
nslookup才看到 - Flask 1.0.1 不支持 SNI → 一个进程只能绑一张证书, 多域名要启多个端口
每条都够写一篇文章, 这次不展开。下次有空单独写 "ZeroSSL + Flask 部署坑大全"。
前后对比
为了直观, 放 3 张图:
图 1: iOS Safari 自签证书红色全屏警告 (改造前)
图 2: 装好 ZeroSSL 后 - 绿色锁标 + ZeroSSL RSA DV SSL CA 2 颁发
图 3: 同一手机, 同一工具, 改造前后 5 秒内访问对比
改造后给同一批 12 个朋友再发链接, 12 个都能进。其中 3 个问我 "你这个是付费工具吗, 怎么没广告?" 我笑了。
结论: 自部署工具必须上 HTTPS, 别省这点事
如果你也跑自部署工具 (不限于百家乐, 任何 Flask / Node / Go / Java 写的 web app), 强烈建议今天就上 HTTPS:
- ZeroSSL 免费 + 90 天 + 自动续期, 没理由不上
- 移动端拦截是隐性的, 你自己测不出来, 用户不会告诉你, 只会默默离开
- HTTPS 锁标是信任信号, 用户看到锁会更愿意输邮箱注册
下周我打算写一篇 HTTPS 加密原理: 用 5 分钟给我妈讲明白, 给非技术朋友看。如果你对 ZeroSSL 续期 SOP 感兴趣, 也留言告诉我。
想直接体验装好证书的 PRO 工具, 可以从 PHD 工具页 进入, 或者参考我们之前的 30 天实战日记 看看用起来什么样。