博客上线几年,一直跑在 http 上。直到前几天想把站点的分享卡片和图片引用理一理,才发现混合内容规则把 http 站点拦在外面,索性把 HTTPS 一起办了。
证书这块,最初看到的是 JoyQi 那篇《彻底放弃收费 SSL 证书》。他公司原本每年在 DigiCert 花上千美元续 OV 证书,后来改用 acme.sh 向免费 CA 申请 DV 证书,配合 GitHub Actions 定时续签,用了一年没有任何兼容性问题。DV 和付费证书的差别只在证书里不显示公司名称——浏览器信任等级完全一样,个人博客没有理由为那行组织名付钱。
他的场景是域名解析在 DNSPod,acme.sh 直接调 DNSPod 的 API 做 DNS 验证,全自动。我照搬不了:域名早年转进了西部数码,而它的 API 是代理/企业向的,个人账号申请不到。好在后来发现根本不需要 DNS 验证,acme.sh 的 nginx 模式走 HTTP 验证,对服务器上跑着 nginx 的站点来说更省事——它临时往 nginx 配置里塞一条验证规则,签完自动还原,不碰 DNS、不需要在域名商开任何接口。
先纠正一个概念
网上把这些免费证书叫「自签证书」,其实不是一回事。自签证书是自己给自己签发,没有受信任的证书机构背书,浏览器一律弹全屏警告,访客看到「不安全」就走了。acme.sh 走的是 ACME 协议,向 Let's Encrypt 或 ZeroSSL 这类公共 CA 申请 DV 证书,浏览器默认信任,地址栏是正常的锁标志。我的两张证书都是 ZeroSSL 签的,有效期 90 天,到期自动续。
在测试机上把流程走通
正式动手前,我在闲置的阿里云裸机上完整练了一遍,确认没坑才碰生产机。
裸机是 Debian 11,先装 nginx 并建一个最小站点,让 test.baikeyang.com 能在 80 端口返回 200:
apt update && apt install -y curl git nginx
mkdir -p /var/www/test && echo ok > /var/www/test/index.html
cat > /etc/nginx/conf.d/test.conf <<'EOF'
server {
listen 80;
server_name test.baikeyang.com;
root /var/www/test;
index index.html;
}
EOF
nginx -t && nginx -s reload然后装 acme.sh。GitHub 直连在大陆机器上经常失败,gitee 镜像一次成功:
git clone https://gitee.com/neilpang/acme.sh.git && cd acme.sh && ./acme.sh --install -m 邮箱安装脚本会自动写一条 cron,每天检查一次证书有效期,临近到期自动续签。装完后当前终端还认不到 acme.sh 命令,重开 shell 或直接用全路径 ~/.acme.sh/acme.sh。
签证书一条命令:
~/.acme.sh/acme.sh --issue -d test.baikeyang.com --nginx第一次跑的时候它报 Cannot find config file for domain。原因是 nginx 虽然装了,但 80 端口回的是默认欢迎页,配置里没有 server_name 匹配这个域名的 server 块,acme.sh 找不到可以临时修改的配置文件。把上面那个 test.conf 建好、reload 之后再签就过了——nginx 模式的前提是它能在配置里找到一个对得上的 server 块。
签发过程大概一分钟:ZeroSSL 注册账号、验证域名、签发下载。日志里会给出证书路径和下次续签时间。
部署到 nginx 并挂上自动重载
签好的证书不直接引用 acme.sh 目录里的文件,而是安装到固定路径,--reloadcmd 会在每次续签后自动执行:
mkdir -p /etc/nginx/ssl
~/.acme.sh/acme.sh --install-cert -d test.baikeyang.com \
--fullchain-file /etc/nginx/ssl/test.cer \
--key-file /etc/nginx/ssl/test.key \
--reloadcmd "nginx -s reload"nginx 配置改成两块:原来的 80 块改成整站 301 跳转,新增 443 块承接真实流量:
server {
listen 80;
server_name test.baikeyang.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name test.baikeyang.com;
ssl_certificate /etc/nginx/ssl/test.cer;
ssl_certificate_key /etc/nginx/ssl/test.key;
root /var/www/test;
index index.html;
}nginx -t && nginx -s reload 之后,浏览器打开 https 地址,锁标志就出来了。
上生产机
博客机是 nginx 1.22 + PHP 8.2-fpm,跑着 Typecho。流程和测试机完全一样,只有一处不同:443 块里的 root、rewrite、php 段要从原 80 块原样照抄,因为每台机器的 php-fpm 接法不一样,我的接的是 unix:/run/php/php8.2-fpm.sock。改配置前先备份了一份原文件,改完 nginx -t 确认语法再 reload,全程线上没有中断。
签发、安装、改配置、重载,四步跑完外网验证:https 返回 200,http 请求 301 跳转,证书有效期到 2027 年 2 月,续签计划自动排到了 1 月下旬。
Typecho 侧的收尾
后台「设置 → 基本」里把站点地址从 http:// 改成 https://。Typecho 有防锁死机制,地址改错也还能进后台改回来,这一步不用怕。
地址改完,站内链接和页面资源就都按 https 输出了。剩下的残留是三类:
- 主题外观设置里的 logoUrl——后台外观设置里存的还是
http://开头的图片地址,浏览器按混合内容规则拦掉,图片显示不出来。进后台把地址改成 https 就好。 - 早期文章正文里的链接——几篇老文章正文里手写的
http://链接,可以后台逐篇改,也可以在数据库里把http://blog.baikeyang.com批量替换成 https。 - 主题文件里写死的地址——页脚有一处模板里写死的
http://链接,改主题文件。
我的处理方式是后台和数据库各改了一部分,本地仓库同步改了主题文件。改完用浏览器全站转一圈,首页、文章页、图片、后台,看地址栏锁标志有没有变成叹号——变成叹号就说明那个页面还有混合内容没清干净。
两个前提条件
这套流程能跑通,有两个服务器之外的条件:
一是 443 端口要在云控制台放行。腾讯云的安全组默认不放行 443,需要在控制台加一条入方向 TCP 443 规则,否则证书签下来了外网也连不上。我是在证书签完之后才发现本机 443 不通,回头看安全组才明白的。
二是 nginx 模式要求签发时 80 端口可达,而且后续每次自动续签都走同一条路。如果站点迁移、80 被占用之类的情况,续签会失败,不过 acme.sh 的 cron 每天都跑,恢复后下一次就会补上。
评论
暂未收到