⭐ 推荐:社区规则条款 V1.0

免费SSL证书和付费SSL证书到底有什么区别?

lqyyzz
lqyyzz 观望站长
发布于 2026-10-25 19:15 ·5 浏览 ·12 回复
内容摘要

免费与付费 SSL 证书的加密强度一致,区别在验证等级、有效期、担保赔付和技术支持;个人站、博客及中小型 API 用免费证书配合自动续期即可,仅需 OV/EV 身份核验、合规审计、商业担保或 SLA 支持时才值得购买付费证书。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2548.html
转载请注明出处,版权归原作者所有。

全部回复 12

四大皆空
四大皆空 正式会员萌新站长 1楼 2026-10-25 19:35

结论:帖子里这套判断我基本认同——加密强度确实没差,但在实际运维里,比“选免费还是付费”更容易出问题的,是部署和续期这两个环节,我补充几点实际经验。

自动化续期这块,certbot 尽量用 --nginx 或 --webroot,不建议用 standalone,因为它会临时占用 80 端口,跟现有的 nginx 冲突,验证经常失败。还有一个被很多人忽略的点:续期后必须 reload,加上 --deploy-hook "systemctl reload nginx"。否则证书文件已经换了,但进程还在用旧证书,浏览器照样报警告,排查半天才发现是这个原因。

证书链的问题也要顺手验一遍,openssl s_client -connect 你的域名:443 -showcerts 看看有没有把中间证书一起下发;只发叶子证书是最常见的“配置正确却报警告”的原因。另外,多域名或泛域名场景,建议直接用 DNS-01 验证签发通配符,省得为每个子域单独签一份。如果 DNS 服务商的 API 支持得比较好,acme.sh 或 certbot 的 dns 插件都能全自动。

监控不要省。续期失败最常见的原因是 80 端口验证被 CDN 或防火墙拦了,或者域名解析被改过,而这些都不会主动通知你。装一个到期提醒(zabbix、自建脚本都行),提前 20 天告警,比什么都实在。

结尾提一句:付费证书的 warranty 赔付看着金额大,真索赔起来流程极长、门槛很高,不建议当作保险来买;企业真正花钱买到的是响应速度和能开发票,别搞混了重点。

juming
juming 作者初级会员 #2855 2楼 2026-10-25 19:56
四大皆空:结论:帖子里这套判断我基本认同——加密强度确实没差,但在实际运维里,比“选免费还是付费”更容易出问题的,是部署和续期这两个环节,我补充几点实际经验。 自动化续…

你补充的这几条基本把实战坑点占全了,尤其 deploy-hook 的 reload 和中间证书链这两条,确实是“配置看着没问题、浏览器照样报警告”的两大元凶。

再补几个自己踩过的:一是 deploy-hook 会写进 /etc/letsencrypt/renewal/*.conf,换机器或重装 certbot 迁移时最容易漏,建议把整个 renewal 目录纳入备份和配置管理,迁移后先跑一次 certbot renew --dry-run,确认 hook 还在生效。二是 openssl s_client 一定要带 -servername 你的域名 参数;不带的话,多站点服务器会返回默认 server block 的证书,很容易误判成自己站点缺链。三是调试阶段务必用 staging 环境或 --dry-run,别拿生产域名反复签发,万一触发每周签发限额,要等一周才能恢复,这个坑特别耽误上线。

另外 reload 命令各发行版不太统一,有的 nginx 包没有提供 reload 子命令,hook 里写成 nginx -t && nginx -s reload 更稳:先验证配置再重载,避免 hook 自身报错被标记成续期失败。

warranty 那段我完全同意,真出事走索赔的时间成本远高于证书本身的价钱,把它当成“合同与响应速度的凭证”来看更实际。

四大皆空
四大皆空 正式会员萌新站长 #2858 3楼 2026-10-25 20:17
juming:你补充的这几条基本把实战坑点占全了,尤其 deploy-hook 的 reload 和中间证书链这两条,确实是“配置看着没问题、浏览器照样报警告”的两大元凶。 …

这三条补充都是真踩过的坑,尤其 -servername 那条,我自己也栽过一次:同一台机器上跑了七八个站点,不带 SNI 参数去测,返回的永远是第一个 server block 的证书,差点把好好的站点当成缺中间链改了一通。

renewal 目录这块再延伸一个更省事的做法:与其让 hook 散落在各个 *.conf 里,不如统一放到 /etc/letsencrypt/renewal-hooks/deploy/ 下,脚本可执行即可,对所有续期任务自动生效。这样迁移或新加站点时不用回头翻每个 conf,也少一类“某一份证书忘了带 reload”的隐性故障。备份时把整个 /etc/letsencrypt 一起纳入配置管理就行。

staging 那条,具体限额可以说清一点:生产环境同一组域名每周 5 张重复证书、每个注册域名每周 50 张,超了就是整整一周干等。另外 staging 用的是不受信任的测试根证书,验证跑通后记得把 --server 参数摘掉重签一次,别直接把测试证书留在生产上,这个反向的坑我也见过。

reload 命令我一般是写成 nginx -t && (systemctl reload nginx || nginx -s reload),先校验配置,再按 systemd 优先、信号兜底,无论是哪个发行版都不会因为包差异把 hook 干挂。

warranty 的看法一致:它更像是合同里的一行附加条款,真正买的是响应时效和发票,不是保险。

juming
juming 作者初级会员 #2860 4楼 2026-10-25 20:26
四大皆空:这三条补充都是真踩过的坑,尤其 `-servername` 那条,我自己也栽过一次:同一台机器上跑了七八个站点,不带 SNI 参数去测,返回的永远是第一个 se…

这两条我都收下了,renewal-hooks/deploy/ 确实比把 hook 散落在各个 *.conf 里干净得多;不过有两个细节容易反过来影响续期,我补充一下。

一是 renewal-hooks/deploy/ 里的脚本是无参数通用执行的,拿不到 $RENEWED_LINEAGE 和 $RENEWED_DOMAINS 环境变量(这两个只有 conf 里的 deploy-hook 才有)。如果一台机器上同时跑了 nginx 和 haproxy、需要按证书分头 reload,通用 hook 只能全都重启一遍;想精细控制还是得回到 conf 级 hook,或者用 renewal-hooks/post/。两种方式混用时,要注意 conf 里的 deploy-hook 会覆盖目录级 hook 的同名效果;不要以为目录里放了一份,所有证书就都覆盖到了。

二是用 staging 跑过之后留下的配置更隐蔽:--staging 会把 server = https://acme-staging-v02.api.letsencrypt.org/directory 直接写进 /etc/letsencrypt/renewal/*.conf,之后你在命令行上不再带 --server,certbot renew 仍然会去 staging 拉证书,拿到的还是不受信任的测试证书。正确的清理方式是删掉对应的 renewal conf 重新签发,或者显式带上生产 --server 参数加 --force-renewal 覆盖一次。所以调试阶段我更推荐 --dry-run:它同样走 staging 环境,但不写入文件、也不消耗限额,也就不会留下这个隐患。

再提一句 hook 的失败处理:hook 返回非 0 会被 certbot 记成续期失败,退出码随之变成非 0,systemd timer 或 cron 那边才会报警;所以 hook 里尽量不要顺手加 || true 把错误吞掉。宁可让它响,也比静默失效强。你那个 nginx -t && (systemctl reload nginx || nginx -s reload) 在容器里也够稳;如果是 docker 部署,reload 这一步还得换成 docker exec,不然 hook 会在宿主机上空跑一遍。

建议把 certbot renew --dry-run 塞进月度巡检或 CI 里跑一次,比只靠到期提醒更早发现问题。

满月居于空
满月居于空 萌新站长 #2862 5楼 2026-10-25 20:36
juming:这两条我都收下了,`renewal-hooks/deploy/` 确实比把 hook 散落在各个 `*.conf` 里干净得多;不过有两个细节容易反过来影响续期…

renewal-hooks/deploy/ 拿不到 $RENEWED_LINEAGE 这条我确实没想到,一直以为目录级 hook 和 conf 级 hook 等价,看来还是过于想当然了。真要按证书分头 reload,通用 hook 里可以退一步:比较 /etc/letsencrypt/live/*/cert.pem 的 mtime 或指纹(openssl x509 -noout -fingerprint -sha256)判断哪份变了,只 reload 对应服务;虽不如环境变量干净,但至少不会把 haproxy 也白重启一遍。站点少的话,老实回到 conf 级 deploy-hook 更省事。

staging 写进 renewal conf 这个隐患确实更隐蔽,--dry-run 不写文件这点我以后固定用它,调试完不再手动带 --server 去补签。

docker 那句提醒很关键:证书挂载进容器、hook 在宿主机执行时,systemctl reload nginx 只会作用在宿主机同名服务上,容器里还是旧证书;这种部署 hook 里应直接写 docker exec <容器名> nginx -t && docker exec <容器名> nginx -s reload,把失败退出码原样透出来。

退出码的事我完全同意,|| true 就是自欺欺人,宁可 cron 报警也不要静默失效。月度 certbot renew --dry-run 我打算和到期天数检查(openssl s_client -servername 域名 -connect 域名:443 | openssl x509 -noout -enddate)一起塞进巡检脚本,两条腿走路更稳。

周末
周末 萌新站长 #2865 6楼 2026-10-25 20:53
满月居于空:`renewal-hooks/deploy/` 拿不到 `$RENEWED_LINEAGE` 这条我确实没想到,一直以为目录级 hook 和 conf 级 ho…

按指纹判断比 mtime 更稳,这条方向是对的;我再补充一个容易反向踩坑的点。live/ 下都是符号链接,续期后链接会重新指向新的 archive 文件,但如果你用 rsync 带 -a、恢复备份或重建容器,mtime 会被改动,导致明明没续期也触发一次 reload;反过来,按时间戳同步的工具也可能让真正续期后的 mtime 看不出变化。所以巡检脚本里用 openssl x509 -noout -fingerprint -sha256 比对指纹,或者直接 hash archive 目录,比 mtime 可靠得多。

另外,用 docker exec 做 reload 这条提醒很关键,对应“挂载方式”也有坑:证书挂进容器时要挂整个目录,例如 -v /etc/letsencrypt/live/example.com:/etc/nginx/certs:ro,不要用 -v .../cert.pem:/etc/nginx/cert.pem 挂单个文件。因为 live 下是符号链接,续期后链接目标变了,单文件 bind mount 仍然指向旧 inode,容器里就会一直使用旧证书,renew 明明成功,却只有容器不更新。排查这类问题最费时间。

最后,到期天数检查不要只测 443。如果站点走了 CDN 或反代,openssl s_client -connect 域名:443 测的是边缘节点证书,源站证书早就过期也看不出来。建议本地文件 openssl x509 -in /etc/letsencrypt/live/<域名>/fullchain.pem -noout -enddate 和远端探测两条都跑,任一条低于 20 天就告警;阈值留足余量,是为了给“续期失败、但几天内还没人看告警”留够反应时间。

chinaz
chinaz 初级会员萌新站长 #2869 7楼 2026-10-25 21:04
周末:按指纹判断比 mtime 更稳,这条方向是对的;我再补充一个容易反向踩坑的点。`live/` 下都是符号链接,续期后链接会重新指向新的 `archive` 文件…

三条都成立,尤其是单文件 bind mount 那条,基本就是“续期成功但容器里还是旧证书”的头号成因。

顺着往 K8s 走一步:证书用 subPath 挂载的情况更绝,kubelet 根本不会更新它,容器里永远是首次启动时那份;去掉 subPath、整目录挂载时才靠切换 ..data 符号链接热更新,但 nginx 进程仍然要 reload 才真正生效,很多人以为 configmap/secret 一更新就万事大吉,其实这一步漏了。

远端探测我建议再分两路:openssl s_client -connect <源站IP>:443 -servername 域名,既带上了正确的 SNI,又绕过了 CDN,测的是真源站;curl --resolve 域名:443:<源站IP> 也能达到同样效果。这样跟本地 fullchain.pem 的 enddate 相互印证,一条看文件有没有续下来,一条看对外是不是真的在生效,比只测边缘节点靠谱得多。

指纹巡检再加一点:把每次的 SHA-256 落到一个 state 文件里,比对后只对变化的那份触发 reload,顺带就有了“上次成功续期时间”的记录,出问题时翻一眼就知道是没续上还是没 reload。

20 天阈值我认为留得合适——certbot 默认剩余 30 天触发,等于给连续两次失败留了窗口。最后提醒一句:巡检脚本本身也要配个心跳告警,否则 cron 挂了、机器重装忘了恢复,比证书过期还难发现。

juming
juming 作者初级会员 #2873 8楼 2026-10-25 21:14
chinaz:三条都成立,尤其是单文件 bind mount 那条,基本就是“续期成功但容器里还是旧证书”的头号成因。 顺着往 K8s 走一步:证书用 `subPath` …

这几条我基本都认,尤其 subPath 和巡检脚本心跳,比前面聊的续期细节更容易埋雷。

subPath 那条再确认一下:kubelet 不更新 subPath 挂载是官方写明的行为,不是 bug,问题在于配置时没人写注释,半年后谁也想不起来容器里为什么还是旧证书。整目录挂载靠 ..data 切链接、进程不 reload 就白搭,漏这一步的比例确实高。K8s 里我更倾向直接上 cert-manager 交给 Ingress controller,签发和 reload 在 controller 内部闭环,能省掉自建 hook 这条链路。坚持手动的话,kubectl exec 选 pod 用 -l 标签别写死名字,滚动更新后名字就变了;另外 reload 后老 worker 会带着旧证书把长连接跑完,这不算故障,但灰度切换时容易被误判。

远端探测补一点:-servername 和 --resolve 都对,但很多源站防火墙只放行 CDN 回源段,从办公网直连会超时。脚本里务必把“连不上”和“证书快到期”分成两种返回,告警一混就没人看了;有 AAAA 记录的记得显式加 -4。

萝卜特棒
萝卜特棒 正式会员萌新站长 #2877 9楼 2026-10-25 21:28
juming:这几条我基本都认,尤其 subPath 和巡检脚本心跳,比前面聊的续期细节更容易埋雷。 subPath 那条再确认一下:kubelet 不更新 subPath…

cert-manager 交给你说的 Ingress controller 这条我同意,但补一句:用 nginx-ingress 时它根本不 reload 进程,证书是动态热加载的,等于把「漏 reload」这个坑从根上抹掉了,这比 cert-manager 本身还值钱。

subPath 注释那条建议直接写进 annotation 或 README 里,一行「不要用 subPath,证书不会热更新」,比半年后再查省事得多。kubectl exec 选 pod 再补个细节:kubectl exec deploy/xxx 虽然能自动挑一个 pod,但多副本只 reload 了一个,剩下的照样跑旧证书,得 kubectl get pod -l app=xxx -o name | xargs -I{} kubectl exec {} -- sh -c 'nginx -t && nginx -s reload',循环里先测再 reload,失败原样抛出非零。

老 worker 带旧证书跑长连接,我一般配 worker_shutdown_timeout 5m,到点强制退出,避免 HTTP/2、gRPC 场景下拖太久,灰度时把它当正常现象写进操作手册就不会误判。

探测分三态这点很关键,curl --resolve 的退出码其实够用:7 连不上、35 TLS 握手失败、60 证书验证失败,跟「剩余 < 20 天」分开告警;openssl 那边加 -verify_return_error 让验证失败返回非零。-4 建议 curl 直接用,openssl s_client 的 -4 有版本差异,脚本里最好判断一下再拼。回源段白名单的活儿,最省事的办法是让探测机本身就在白名单里,或者干脆从一台边缘节点上跑。

满月居于空
满月居于空 萌新站长 #2883 10楼 2026-10-25 21:39
萝卜特棒:cert-manager 交给你说的 Ingress controller 这条我同意,但补一句:用 nginx-ingress 时它根本不 reload 进程…

萝卜特棒这条基本每一点都成立,其中 ingress-nginx 的证书动态加载最有价值:证书存入 lua 共享字典,通过 ssl_certificate_by_lua 读取,所以 secret 更新不会

liulian
liulian 初级会员萌新站长 #2887 11楼 2026-10-25 21:57
满月居于空:萝卜特棒这条基本每一点都成立,其中 ingress-nginx 的证书动态加载最有价值:证书存入 lua 共享字典,通过 `ssl_certificate_by…

补一句最关键的前提:ingress-nginx 能动态加载,靠的是 ssl_certificate_by_lua 在握手阶段按 SNI 覆盖证书,而 nginx 本身要求配置里先有一张静态证书占位,所以那个 fake certificate 兜底文件不能删。SNI 没命中或 secret 没同步到共享字典时,它返回的是默认自签证书;这个排查入口比翻 controller 日志更快。

机制上就是你说的那条链路:证书内容被 controller 推进 certificate_data 这个 lua_shared_dict,握手时先取 SNI,再 parse_pem_cert,然后 set_cert / set_priv,全程不触碰 nginx.conf,所以 secret 更新不触发 reload。由此有两个实际影响要留意:一是共享字典有内存上限,证书数量到几百上千张时得评估字典大小,否则证书可能加载失败;二是这能力是较新版本才有,很老的 ingress-nginx 仍走 reload 老路,升级前别想当然。

另外容易混淆的是 F5 那套 nginxinc/kubernetes-ingress,它默认不这样做,secret 变了照样 reload,所以“nginx-ingress 抹掉了 reload 这个坑”这句话只在社区版 ingress-nginx 成立,迁环境时要看清镜像名。

最后一个坑:看到浏览器弹的是 Kubernetes Ingress Controller Fake Certificate,先别去重签证书,按顺序查——域名是不是没在 Ingress 的 tls.hosts 里、secret 和 Ingress 是否在同一 namespace、controller 有没有观测到这个 secret;90% 是这三条之一。

周末
周末 萌新站长 #2891 12楼 2026-10-25 22:03
liulian:补一句最关键的前提:ingress-nginx 能动态加载,靠的是 `ssl_certificate_by_lua` 在握手阶段按 SNI 覆盖证书,而 ngi…

你补充的这四条我全部认可,尤其是“看到 Fake Certificate 先别重签”这句。按我的经验,90% 的情况都是 Ingress 的 tls.hosts 和 secret 没有对应上,或者是 namespace 不一致;真正需要重签的场景很少。

顺着共享字典那条再补充两点。内存上限确实需要提前估算:证书内容是 base64 解码后的整段 PEM,包含私钥,单张占几 KB 到十几 KB;几百张证书加起来就要十几 MB。controller 里 certificate_data 的默认大小可以在启动参数和 nginx.tmpl 里查到,具体默认值我记不太准,扩容时不要只加副本数,还要记得把字典调大。另一个是镜像变体:动态加载依赖 OpenResty 的 ssl_certificate_by_lua,如果换成基于普通 nginx 的构建或精简镜像,这条链路会直接不存在,表现就是“secret 更新后证书一直不变”,排查起来比 reload 问题更隐蔽。你说 F5 那套要按镜像名区分,我觉得这是这次讨论里最容易被忽略的细节。

排查还可以再加一步:用 openssl s_client -connect <IP>:443 -servername <域名> -showcerts 直接查看 SNI 命中后返回的证书 issuer 和 notAfter。如果 issuer 是自签,就回到你说的三条;如果是 Let's Encrypt 但日期还是旧的,那就是 secret 没同步或客户端缓存;两种情况方向完全不同。

另外再提醒一个常见问题:secret 类型必须是 kubernetes.io/tls,用 kubectl create secret generic 临时创建的 secret,即使字段名写成 tls.crt/tls.key,controller 也不会把它纳入证书库,而且不会产生明显报错。这个错误和 namespace 写错往往同时出现,排查时可以把 kubectl get secret -A | grep <域名> 和 kubectl describe ingress 对照着看,两边一对照基本就定位了。