很多年前我第一次向某免费证书签发机构申请CA证书时,以为就是把一个文件下载下来、传到服务器上改改配置那么简单。结果那一次我对着浏览器里的“不安全”警告折腾了整整一个下午才找到原因——证书链没合并完整。后来陆陆续续帮同事、帮客户部署过不少证书,DV、OV、商业CA、免费证书都碰过,总算把这个流程玩明白了。这篇记录就是把一次完整的CA证书申请和服务器部署过程从头到尾拆开讲清楚,包括每一步的原理、命令、参数、踩坑点,以及那些文档里从来不写的细节。
这篇东西适合正准备给网站上HTTPS、但还没实际操作过的人看,也适合那些已经配过一次但搞不清楚为什么偶尔会报证书错误的运维和开发者。我尽量用大白话把私钥、CSR、证书链、自动续期这些概念讲透,跟着步骤走下来,你至少能独立完成一次证书的申请和部署,并且遇到常见报错时知道往哪个方向排查。
1. 申请前先想明白:你需要的到底是哪种证书
很多人在第一步就栽了——直接去CA下载了一个证书,结果安装到一半发现类型不对、域名对不上、用途也不匹配。证书这玩意儿不是随便拿一个就能跑的,先花五分钟搞清楚自己要什么,比什么都重要。
1.1 三种证书怎么选:DV、OV、EV
CA证书按验证级别从低到高分为DV、OV、EV三种,对大多数普通站点来说,选DV就够了。
DV(域名验证)只验证“你对这个域名有没有控制权”,过程几乎全自动,几分钟到几个小时就能签发,免费证书基本都是这种。适合个人博客、企业官网、API服务、后台系统——只要你需要在公网上提供HTTPS服务,DV完全够用。OV(组织验证)在域名验证之外还要验证企业主体信息,比如营业执照、联系人身份,签下来的证书里会带上组织名称,地址栏能显示企业名。适合电商、P2P金融、政府服务这类需要用户信任背书的场景。EV(增强验证)要求最高、价格最贵,浏览器地址栏会显示绿色公司名,但现在主流浏览器对EV标识的展示越来越弱,性价比已经不如以前了。
我的建议很简单:默认选DV,除非平台方或者合规要求明确指定OV/EV。证书的功能本质都是加密通信,DV和OV在加密强度上没有任何差别,OV多出来的只是“你是谁”的可信度背书。如果只是自己的服务,选DV就完事了,没必要多花钱。
1.2 自签名证书和第三方CA证书的本质区别
有朋友问过我:不申请CA证书,用openssl自己生成一个自签名证书行不行?技术上当然行,配置方式也一样能跑起来,但有一个核心问题:浏览器和系统不认你。
自签名证书的信任链断了。浏览器内置了一个“信任的根证书列表”,里面装的是各大CA机构的根证书。你自己的证书没有经过任何CA签名,浏览器不信任它,访问时会直接拦下来一个红色警告页面。即便你把自己的证书导入本地信任列表,别人访问你的网站照样警告。
而第三方CA签发的证书之所以能被浏览器接受,是因为它的证书链最终能追溯到某个内置于客户端的根证书。这个“信任链”就是CA体系的核心价值:CA机构用自己的根证书私钥给下级证书签名,下级CA再给你的证书签名,你的证书就带着一串“担保人”信息,最终被浏览器认可。
所以只要你的服务会暴露在公网上,就别琢磨自签名了。测试环境可以凑合,正式环境老老实实申请CA证书。
1.3 预算怎么定:免费证书还是商业证书
证书不一定花钱。现在免费证书的质量已经非常可靠,我本人主力就用的免费证书,跑了几年没出过事。商业证书贵则一年几千块,到底差在哪?免费证书通常有效期短(90天),需要自动续期,如果续期机制没做好,到期忘了就挂。商业证书一般1~2年有效期,省心一些,但现在的业界趋势也是推动短命证书,因为短命证书的安全边际更好。商业CA提供技术支持,出问题可以找人工。免费证书基本靠自己,好在社区资源多,常见问题搜一下都有。如果是内部系统或者博客这种非关键业务,免费证书绰绰有余。如果是给银行、大企业做服务的甲方,对方指定要某商业CA,那就按规矩买。
2. 从生成私钥到拿到证书:申请流程全拆解
确定好证书类型之后,真正的申请流程开始了。这一步里面最容易混乱的是两对概念:私钥和CSR的关系、公钥和证书的关系。我一边走流程一边说明。
2.1 准备阶段:域名解析和Web服务连通性
证书申请之前有两件事必须确认:域名要能正常解析到你当前服务器,该域名对应的Web服务已经可以响应HTTP请求。
为什么这两件事这么重要?因为CA验证时,最常见的两种方式都会实际访问你的域名和服务器:
- 如果走DNS验证,CA需要你给域名配置一条特殊的TXT解析记录,DNS解析本身就是前置条件。
- 如果走HTTP文件验证,CA会访问
http://你的域名/某随机路径/验证文件,服务器上必须已经跑着Web服务,且这个路径能公开访问。
所以不要在域名还在备案、还没上线的时候去申请证书,白等而且容易出错。建议先确认:ping 你的域名能返回服务器IP;浏览器直接访问http://服务器IP能打开你网站的默认页面,如果还不能打开,先把这个搞定再谈证书。
2.2 用openssl生成私钥和CSR
拿到证书的前提是要交一份CSR(证书签名请求)给CA。CSR可以理解成一份“开证书的申请表格”,里面包含:
- 你的域名信息,Common Name(CN)字段就是主域名。
- 你的公钥。
- 你希望CA签发的证书包含哪些扩展,比如Subject Alternative Name。
生成CSR之前需要先有一对密钥对。命令很简单:
openssl req -new -newkey rsa:2048 -nodes \ -keyout example.com.key \ -out example.com.csr \ -subj "/CN=example.com"逐个解释一下:
rsa:2048表示用RSA算法生成2048位密钥。2048位是当前的安全基线,别再用1024,那是历史遗留的弱密钥。-nodes意思是“不加密私钥文件”——别把整个逻辑搞反,这里说的是私钥本身不做密码保护,否则每次Nginx启动都要你输一遍密码,服务器重启就爬不起来了。-subj直接指定证书主体信息,避免openssl交互式地弹出问题让你一个个填。/CN=example.com这个CN字段一定要和你的域名完全一致。
如果还需要给多个域名签同一张证书,比如 example.com 和 www.example.com 都要用,把-subj换成下面的形式:
openssl req -new -newkey rsa:2048 -nodes \ -keyout example.com.key \ -out example.com.csr \ -subj "/CN=example.com" \ -addext "subjectAltName=DNS:example.com,DNS:www.example.com"这一步生成的.key是你服务器上的命根子:私钥一旦丢失或泄露,证书就等于白签,必须作废重签。所以生成之后权限立刻收紧,后面部署时会专门讲怎么放。
提示:私钥和CSR永远不要通过邮件、聊天工具传输。你就把它当银行卡密码一样看待就对了。
2.3 三种域名验证方式对比与选择
CA收到CSR之后不会立刻签证书,它要先确认“这个域名真的是你的”。验证方式主流有三种,它们各有优劣,我在不同场景下都用过:
DNS验证(TXT记录)
CA告诉你:请为example.com添加一条TXT记录,内容是一串随机字符串。你在域名解析控制台添加后,CA检测到记录匹配,验证通过。
优点是一劳永逸,尤其适合通配符证书(*.example.com),这是唯一支持通配符的验证方式。缺点是操作步骤多,如果DNS解析有缓存,可能要等几分钟到几小时。
HTTP文件验证
CA生成一个随机文件,比如.well-known/acme-challenge/xxx,要求你把这个文件放到网站的根目录下,然后CA去访问这个路径。
优点是不需要动DNS,解析生效后操作很快,适合验证单个域名。缺点是对每个域名都要放一次文件,而且要求Web服务已经正常工作,443或80端口都是通的。
邮箱验证
CA给域名WHOIS里注册邮箱或常用管理邮箱发一封确认信,你点链接确认。
优点是操作最简单,缺点是如果域名邮箱收不到信就很麻烦,而且现在CA越来越不推荐这种低强度验证,很多已经不支持了。
我的选择原则:单域名用HTTP文件验证,多域名或通配符用DNS验证,邮箱验证能不用就不用。后者等待时间不可控,体验极差。
2.4 提交审核到拿到证书
验证方式选好后,提交CSR、完成验证、等待签发,这个过程短则一两分钟,长则OV/EV可能等几天。以DV证书为例,大概流程是:
- 把CSR内容粘贴到CA的申请页面,或通过API提交。
- 选择验证方式,完成对应操作。
- 等待CA的自动验证程序跑一遍,一般10分钟以内。
- 通过后下载证书文件。
下载下来的文件最多可能有三个:example.com.pem(你自己的证书)、chain.pem(中间证书链)、fullchain.pem(前两者合并的完整链)。如果你拿到的是分开的文件,不用慌,后面部署时把它们按顺序合并成一个就行。原理我在下一章说清楚。
3. 部署到服务器:Nginx配置HTTPS的完整过程
证书拿到手,真正的实战才开始。我见过太多人卡在这一步,要么配置完浏览器报“无法建立安全连接”,要么证书能装上但每次重启服务就起不来。这一章把部署的每一步做完整拆解,以Nginx为例,因为我日常主力就是它,其他Web服务器逻辑大同小异。
3.1 证书文件怎么放、权限怎么设
先把证书文件整理到规范位置。我习惯的目录结构是:
/etc/ssl/certs/example.com.fullchain.pem /etc/ssl/private/example.com.key别一股脑全扔/root下,也别放在Web根目录里。这两个路径是有讲究的:/etc/ssl/certs是公钥证书的标准存放目录,权限宽松一点没关系;/etc/ssl/private是私钥的专用目录,默认权限就是任何人都不能读。
上传之后立刻执行权限收紧:
chmod 644 /etc/ssl/certs/example.com.fullchain.pem chmod 600 /etc/ssl/private/example.com.key chown root:root /etc/ssl/private/example.com.key为什么要做这一步?私钥文件只要权限宽松一点,Web服务进程或同服务器的其他用户就可能读走它。私钥一旦泄露,就等于任何拿到私钥的人都能伪装成你的服务器进行中间人攻击,CA证书等于白配了。权限这件事,一次都不能偷懒。
3.2 Nginx的HTTPS配置与参数选择
在Nginx中启用HTTPS,核心就是告诉它“证书放哪、私钥放哪、用哪些加密协议”。我直接给一份能直接用的server块:
server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name example.com; ssl_certificate /etc/ssl/certs/example.com.fullchain.pem; ssl_certificate_key /etc/ssl/private/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; # 站点其他配置... }这里有几个点挨个说明白:
为什么用fullchain.pem而不是example.com.pem?因为浏览器验证你的证书时,需要沿着证书链找到CA的根证书。example.com.pem只包含你自己那一段,如果不带上中间证书,浏览器的信任链就断了,访问会报“证书链不完整”。fullchain.pem就是你自己的证书 + 中间证书按顺序合并,所以Nginx里直接指它。如果你下载的是分开的文件,手动合并:
cat example.com.pem chain.pem > example.com.fullchain.pemssl_protocols配什么?答案只有TLSv1.2 TLSv1.3。TLSv1.0、TLSv1.1都已经淘汰多年,旧浏览器兼容性带来的收益远小于安全风险。什么都不写的话,Nginx在高版本里默认也比较安全,但显式写出来方便团队里其他人理解你的安全基线。
ssl_ciphers要不要花大力气调?对大多数人来说,用上面我写的那一行就够。除非你是安全服务商要过等保、合规审计,否则不需要背一串长长的密码套件列表,既难维护,展示效果也有限。
配置改完之后测试并重载:
nginx -t systemctl reload nginx务必先跑一遍nginx -t,Nginx会告诉你配置语法有没有问题。配置有问题时直接reload会失败,服务还保持旧配置,容易产生“我明明改了怎么没生效”的错觉。
3.3 强制跳转与HSTS:别漏掉的细节
证书部署好之后,还有两件收尾的事:强制HTTP跳转HTTPS,以及开启HSTS。
强制跳转我已经写在上面第一个server块里了,就是那句return 301 https://$host$request_uri;。这行的意思是:所有80端口的请求,直接307还是301,我用的是301——告诉浏览器“资源永久迁移到HTTPS地址”,这样浏览器记住之后就不会再走HTTP。注意这里我没有把server的配置写成listen 443 ssl http2之类的写法,301跳转最稳,兼容性最好。
HSTS是更激进的一层保护:它告诉浏览器“这个域名在接下来一段时间内,只允许用HTTPS访问”,即使用户手动输入http://,浏览器也会在本地直接改成HTTPS请求,根本不会发HTTP请求出去。在Nginx配置里加一行响应头即可:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;max-age=31536000是1年,includeSubDomains表示所有子域名也适用。这行谨慎开启:一旦浏览器记住,你在测试环境想退回HTTP就要等有效期过期,用户那边也一样。所以我建议先不加includeSubDomains,先把主站跑稳几天再补上。
3.4 部署完成后的验证命令
配置完成别急着宣布“搞定”,用命令行实际验一把。我惯用的验证三板斧:
# 1. 用curl看证书信息和握手状态 curl -I https://example.com # 2. 用openssl深入检查证书链 openssl s_client -connect example.com:443 -servername example.com # 3. 模拟浏览器SSL检测 openssl s_client -connect example.com:443 -servername example.com -briefopenssl s_client输出会很长,重点看几个字段:Verify return code: 0 (ok)说明证书链验证通过;subject和CN要包含你的域名;issuer是签发机构信息。如果返回的是20 (unable to get local issuer certificate),基本就是证书链不完整,赶紧检查fullchain有没有合并对。
4. 自动化续期与高频问题排查
证书部署成功只是开始,后面的日子才是对系统的长期考验——尤其是免费证书,90天有效期摆在那,不搞自动化,迟早有一天早上起来发现网站挂了、浏览器一片红色警告。
4.1 为什么必须自动化续期
我知道有人是这么干的:日历上设置一个“3个月后”的提醒,到期前手动走一遍申请流程。头一两次还行,时间一长总有忘记的时候。而且手动流程的问题不止是“忘”,还有效率——每次都要重新生成CSR、重新验证、重新上传,重复劳动消耗的精力其实比想象中大。
免费的短有效期证书本身就在推动自动化。正规的做法是申请一个与域名验证自动对接的客户端,比如 acme.sh 这类工具,它可以自动完成“生成CSR → 申请 → 验证 → 下载证书 → 重载Web服务”整个闭环。
从信任链的角度看,自动化还有一个隐藏好处:证书续期时如果私钥发生了轮换,新私钥和新证书自动部署到位,比等你手动想起来再去翻老配置要可靠得多。
4.2 配置自动续期的完整步骤
acme.sh 是我用过最顺手的免费证书自动化工具,逻辑就是“定时检查证书有效期,剩不到30天时自动续期,续完自动重载服务”。实际步骤大致是这样:
# 1. 安装工具 curl https://get.acme.sh | sh # 2. 申请证书(HTTP文件验证模式) ~/.acme.sh/acme.sh --issue -d example.com \ --webroot /var/www/html # 3. 安装证书到指定目录,并自动重载Nginx ~/.acme.sh/acme.sh --install-cert -d example.com \ --key-file /etc/ssl/private/example.com.key \ --fullchain-file /etc/ssl/certs/example.com.fullchain.pem \ --reloadcmd "systemctl reload nginx"第2步的--webroot /var/www/html就是让工具把验证文件自动放到网站根目录,对应我前面说的HTTP文件验证。第3步的--install-cert不光是把文件复制过去,还会修改权限到合理状态,然后执行重载命令。
工具装好之后,它会自己往crontab里加一个定时任务,不用你管。但我还是建议每次续期后看一眼日志,确认真续上了:
~/.acme.sh/acme.sh --cron如果哪次验证失败,立刻排查的优先级是:域名解析是否正常、服务器的80/443端口是不是被防火墙挡了、Web服务是否还活着。这几个问题每次都会排在最前面。
4.3 常见故障速查表
下面这些故障我从实际踩坑和帮人排查中收集归纳,写成一个速查表,对照着找问题比我逐条描述更省时间:
| 现象 | 常见原因 | 处理建议 |
|---|---|---|
| 浏览器报“证书不受信任” | 证书链不完整,或服务器只配了叶证书 | 换成fullchain.pem;用openssl s_client验证链 |
| 报“域名不匹配” | 证书里的域名和服务器的域名不一致 | 检查CN和SAN;通配符证书要确认匹配规则 |
| 手机访问正常、老电脑报错 | 客户端不支持TLSv1.2以上 | 确认没开旧协议;确认系统时间正确 |
| 访问整个超时 | 443端口未放行 | 检查防火墙和安全组;用telnet 你的域名 443测 |
| 证书显示已过期但明明刚续过 | 服务器系统时间不对 | 执行date看服务器时间;同步时间源 |
| 续期验证一直失败 | DNS解析有缓存、验证文件路径不对 | 清缓存;确认webroot参数和实际站点根目录一致 |
| 配置好了服务重启失败 | nginx -t没过、证书路径写错 | 先在命令行跑nginx -t,看具体报错行 |
这里面最隐蔽的一个坑是服务器时间不同步。有一次我排查了半天,证书明明有效,openssl客户端反正告诉我证书已过期,后来发现是那台机器的时间慢了两天,导致证书的“有效期判断”全线错乱。这个坑你光看配置是看不出来的,排查时养成先看时间的习惯,能省一大半力气。
另外有个容易忽视的点:证书续期往往不只是换文件,私钥可能也跟着换。如果你把私钥路径写死在Nginx里,而续期工具覆盖了新的私钥文件,Nginx配置不需要改路径,因为文件名不变,但文件内容已经变了。前提是你用的工具把新私钥写到了同一个路径,所以别手贱自己把证书安装目录改得七零八落,保持路径稳定,自动化才能稳定。
写在最后的一点体会
证书申请和部署这件事,说难真的不难,几个小时就能从头玩到尾;说简单也不简单,里面零零碎碎的细节能磨掉你一个周末。我个人最大的体会是:证书不是配完就结束的资产,而是一项需要维护的设施。私钥的保管、证书链的完整、续期的自动化、服务时间的校准,每一环掉链子,整个HTTPS就等于白配。
最后再分享一个小技巧:每次续期或者首次部署之后,顺手把当时的证书指纹记下来,下次出问题的时候对比一下指纹就知道证书有没有被偷偷换过。命令是openssl x509 -in 证书文件 -noout -fingerprint,多花十秒钟,排查的时候少走好多弯路。
希望这整篇记录能帮你少踩几个坑。如果你按步骤走下来遇到了别的报错,拿openssl s_client的返回结果对照我那个速查表排查一遍,大概率能找到答案。