服务器SSL证书安装教程

结论: 服务器上安装 SSL 证书的标准流程是四步:生成私钥与 CSR、向 CA 提交申请完成域名验证、下载签发好的证书文件、在 Web 服务器里配置证书与私钥路径并重载服务。绝大多数安装失败都不是流程错,而是证书链不完整、私钥与证书不匹配、中间证书缺失这三类问题。

HTTPS 早已不是"要不要做",而是"不做就出局"。截至 2026 年,主流浏览器对所有 HTTP 页面都会明确标记为"不安全",Chrome 与 Edge 甚至会对表单输入框给出额外警告;同时微信小程序、App Store 的 ATS 策略、以及各类支付回调接口都强制要求 HTTPS。

但"装证书"这一步对很多人来说依然是个黑盒:CA 给了好几个文件不知道用哪个,Nginx 配完还是提示证书不受信任,Tomcat 只认 Java Keystore 格式束手无策。本教程以 Nginx 1.26、Apache 2.4、Tomcat 10、IIS 10 四种主流 Web 服务器为基准,把完整流程和每个平台的差异讲透。

第一步:搞清楚你要装的是哪一种证书

SSL/TLS 证书按验证强度分为 DV、OV、EV 三档,区别不在于加密强度——三者都能达到 AES-256 级别加密,而在于 CA 对申请者身份的审核深度。 选错档不会让网站更安全,只会多花钱或达不到合规要求。

DV/OV/EV 证书对比表

对比项DV(域名验证)OV(组织验证)EV(扩展验证)
验证内容仅验证域名控制权域名 + 企业营业执照等组织真实性域名 + 严格的组织/法律/物理存在核查
签发时间几分钟 ~ 1 小时1 ~ 5 个工作日1 ~ 7 个工作日
浏览器表现地址栏锁标地址栏锁标 + 证书详情显示公司名锁标 + 证书详情显示公司名(新版浏览器已取消绿色地址栏)
适用场景个人站、博客、测试环境、API 接口企业官网、中小企业业务系统金融、支付、电商、政府采购等强信任场景
参考价格(截至 2026 年 1 月)免费 ~ 数百元/年数千元/年数千至上万元/年,以官网实时报价为准
支持通配符支持支持支持(部分 CA 限制)

必须澄清两个流传很广的误解:

  • 误解一:EV 证书能提升 SEO 排名。 不能。搜索引擎官方从未把证书类型作为排名加权因子,HTTPS 本身才是(且权重极小)。
  • 误解二:EV 更安全。 加密强度与 DV 完全一致。EV 的价值在于"身份可被人工核验",适合需要向客户证明"这个网站确实属于某公司"的场景。自 Chrome 77 起,EV 的绿色地址栏已被移除,只能在证书详情里看到组织名。

域名覆盖范围的三种形式

类型覆盖范围示例说明
单域名(Single Domain)一个完全限定域名www.example.com注意很多 CA 的默认免费单域名证书不含裸域名,需两者都在 SAN 里
多域名 SAN/UCC最多数百个不相关域名example.com、demo.cn、a.net一张证书打包多个域名,便于统一管理
通配符 Wildcard一级通配 *.example.com匹配 blog、shop 等任意一级子域名不匹配裸域名 example.com,也不匹配 a.b.example.com 二级子域名

**重要提醒:通配符证书 *.example.com 不包含 example.com。** 需要两者都覆盖时,申请时必须同时填写两个 SAN 条目:*.example.com 和 example.com。这是证书不匹配报错的常见来源之一。

第二步:生成私钥与 CSR

CSR(Certificate Signing Request,证书签名请求)里只有公钥和申请信息,私钥永远留在你的服务器上,绝对不要发给 CA。 这步做错会导致证书无法使用且必须重签。

bash
# 创建证书目录并限制权限
sudo mkdir -p /etc/ssl/private /etc/ssl/certs
sudo chmod 700 /etc/ssl/private

# 方式一:一条命令同时生成 2048 位私钥与 CSR(推荐 RSA 2048 或 ECDSA P-256)
openssl req -new -newkey rsa:2048 -nodes \
  -keyout /etc/ssl/private/example.com.key \
  -out /etc/ssl/certs/example.com.csr \
  -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=Example Tech Co., Ltd./OU=IT Dept/CN=example.com"

# 方式二:已有私钥,用配置文件追加 SAN 后再生成 CSR(多域名/通配符必用)
cat > /tmp/openssl.cnf <<'EOF'
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C  = CN
ST = Guangdong
L  = Shenzhen
O  = Example Tech Co., Ltd.
CN = example.com

[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = *.example.com
EOF

openssl req -new -sha256 \
  -key /etc/ssl/private/example.com.key \
  -out /etc/ssl/certs/example.com.csr \
  -config /tmp/openssl.cnf

CSR 里几个字段的含义与填写规范:

  • C(Country):两位国家代码,中国填 CN。
  • ST(State/Province)、L(Locality):省份与城市,用英文填写。
  • O(Organization):OV/EV 证书必须与营业执照上的企业全称完全一致,否则会被 CA 驳回。
  • CN(Common Name):主域名。自 2018 年起浏览器已不再看 CN,全部以 SAN 为准,但 CA 仍要求填写。
  • subjectAltName:现代浏览器唯一的域名匹配依据,所有要覆盖的域名必须写在这里。

生成后务必自检再提交,避免来回收发的时间成本:

bash
# 查看 CSR 内容,重点确认 CN 与 SAN
openssl req -in /etc/ssl/certs/example.com.csr -noout -text | grep -A2 "Subject Alternative Name"
openssl req -in /etc/ssl/certs/example.com.csr -noout -subject

# 校验私钥格式与强度
openssl rsa -in /etc/ssl/private/example.com.key -check -noout

第三步:Nginx 安装 SSL 证书

Nginx 需要两个文件:证书文件(含自己 + 中间证书的完整链)和私钥;ssl_certificate 必须指向完整的 fullchain,否则部分客户端报不受信任。 这是 Nginx 上排名第一的配置错误。

CA 提供的文件通常有三个:example.com.crt(域名证书)、ca_bundle.crt(中间/根证书链)、example.com.key(你自己的私钥)。按下面方式合并:

bash
# 合并顺序必须是:域名证书在前,中间证书在后
cat example.com.crt ca_bundle.crt > /etc/ssl/certs/example.com_fullchain.crt

# 校验私钥与证书的公钥是否匹配(三条命令输出必须完全一致)
openssl x509 -noout -modulus -in /etc/ssl/certs/example.com_fullchain.crt | openssl md5
openssl rsa  -noout -modulus -in /etc/ssl/private/example.com.key          | openssl md5
openssl req  -noout -modulus -in /etc/ssl/certs/example.com.csr            | openssl md5

# 校验整条证书链是否完整
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/example.com_fullchain.crt
# 期望输出:example.com_fullchain.crt: OK

完整的 Nginx HTTPS 站点配置,包含强制跳转、HTTP/2、HSTS 与安全加固:

nginx
# HTTP 全部 301 跳 HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;                       # Nginx 1.25.1+ 用此写法;旧版本写 listen 443 ssl http2
    server_name example.com www.example.com;

    ssl_certificate     /etc/ssl/certs/example.com_fullchain.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # 仅 TLS 1.2/1.3,禁用已废弃协议
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # 会话复用:降低握手开销
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # OCSP Stapling:把证书状态查询结果随握手返回,加快验证
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 223.5.5.5 8.8.8.8 valid=300s;
    resolver_timeout 5s;

    # HSTS:告诉浏览器半年内只用 HTTPS 访问(上线稳定后再开,回退成本高)
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

配置完成后:

bash
sudo nginx -t && sudo systemctl reload nginx

# 在线验证握手与链完整性(看 Verify return code 是否为 0)
openssl s_client -connect example.com:443 -servername example.com /dev/null | openssl x509 -noout -dates -subject -issuer

# 用 SSL Labs 在线评分(目标 A 及以上)
# https://www.ssllabs.com/ssltest/analyze.html?d=example.com

第四步:Apache / Tomcat / IIS 三个平台的安装要点

三个平台的共同点是都需要"私钥 + 完整证书链",差异只在于文件格式和加载方式。 Apache 用 PEM 明文即可,Tomcat 需要 Java Keystore(JKS 或 PKCS12),IIS 需要 Windows 的 PFX 容器格式。

Apache 2.4

Apache 需要三个独立文件路径,且必须先启用 mod_ssl:

bash
sudo a2enmod ssl ssl_session_cache headers
sudo systemctl restart apache2
apache

    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent / https://example.com/



    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com

    SSLEngine on
    SSLCertificateFile      /etc/ssl/certs/example.com.crt
    SSLCertificateKeyFile   /etc/ssl/private/example.com.key
    SSLCertificateChainFile /etc/ssl/certs/ca_bundle.crt

    SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
    SSLHonorCipherOrder     off
    SSLSessionTickets       off

    
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    

注意:Apache 2.4.8 起 SSLCertificateChainFile 已废弃,可把中间证书直接追加到 SSLCertificateFile 指向的文件末尾。老版本必须保留该指令。

Tomcat 10

Tomcat 只认 Keystore,需要先用 OpenSSL 合成 PKCS12 再用 keytool 转成 JKS:

bash
# 1) 合成 PKCS12(-name 指定别名,下一步要用)
openssl pkcs12 -export \
  -inkey /etc/ssl/private/example.com.key \
  -in /etc/ssl/certs/example.com.crt \
  -certfile /etc/ssl/certs/ca_bundle.crt \
  -name tomcat \
  -out /etc/ssl/private/example.com.p12
# 会提示设置导出密码,务必记录

# 2) 转成 Java Keystore(Tomcat 10 推荐直接使用 PKCS12,无需转 JKS)
keytool -importkeystore \
  -srckeystore /etc/ssl/private/example.com.p12 -srcstoretype PKCS12 \
  -destkeystore /etc/ssl/private/example.com.jks -deststoretype JKS \
  -srcalias tomcat -destalias tomcat

然后修改 $CATALINA_HOME/conf/server.xml:

xml

    

重启 Tomcat 后检查 catalina.out 有无 Alias name [tomcat] 的输出,那表示私钥已成功加载。JDK 17 及以上建议直接用 keystoreType="PKCS12",可以跳过 JKS 转换这一步。

IIS 10(Windows Server 2022)

IIS 需要在 Windows 证书存储里导入 PFX,再绑定到站点。PowerShell 方式:

powershell
# 1) 先在 OpenSSL 侧生成 PFX(也可在 Linux 上生成后传到 Windows)
# openssl pkcs12 -export -out example.com.pfx -inkey example.com.key -in example.com.crt -certfile ca_bundle.crt

# 2) 以管理员权限 PowerShell 导入到本地计算机证书存储的 "My" 容器
$pwd = ConvertTo-SecureString -String "你的导出密码" -Force -AsPlainText
Import-PfxCertificate -FilePath C:\cert\example.com.pfx `
    -CertStoreLocation Cert:\LocalMachine\My -Password $pwd -Exportable

# 3) 绑定到 IIS 站点(WebAdministration 模块)
Import-Module WebAdministration
$binding = Get-WebBinding -Name "Default Web Site" -Protocol "https"
if (-not $binding) {
    New-WebBinding -Name "Default Web Site" -Protocol "https" -Port 443 -SslFlags 1
}
$binding = Get-WebBinding -Name "Default Web Site" -Protocol "https"
$binding.AddSslCertificate("证书指纹Thumbprint", "my")

# 4) 禁用老旧协议(写入注册表后需重启)
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Force
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Name "Enabled" -Value 0

证书指纹(Thumbprint)可以在"证书管理器 > 本地计算机 > 个人 > 证书"里双击证书查看,或在 PowerShell 中用 Get-ChildItem Cert:\LocalMachine\My 获取。IIS 上证书链缺失通常表现为"Windows 能正常访问,安卓/iOS 报不受信任",基本都是中间证书没安装到 Cert:\LocalMachine\Intermediate 导致的。

第五步:证书链完整性、到期巡检与自动续期

证书链不完整是 HTTPS 故障的头号原因,占实际排错案例的一半以上。 它的诡异之处在于:桌面 Chrome 会因为 AIA 追补特性自动下载缺失的中间证书从而显示正常,但 curl、Java、Android WebView 不会,于是"我电脑上是好的"成了最误导人的证据。

排错与验证的黄金三条命令:

bash
# 1) 查看服务端实际下发了几张证书。返回 1 张说明缺中间证书
openssl s_client -connect example.com:443 -servername example.com -showcerts /dev/null | grep -c "BEGIN CERTIFICATE"

# 2) 检查主题(subject)与颁发者(issuer)能否首尾相接
openssl s_client -connect example.com:443 -servername example.com /dev/null | openssl x509 -noout -subject -issuer

# 3) 验证握手状态(Verify return code: 0 (ok) 才算通过)
openssl s_client -connect example.com:443 -servername example.com /dev/null | grep "Verify return code"

到期巡检建议做成定时任务,而不是靠人记着。下面是一个可放进 crontab 的轻量脚本:

bash
#!/usr/bin/env bash
# /opt/scripts/check-cert-expiry.sh —— 每天检查证书剩余天数,低于阈值发告警
DAYS_LEFT=$(echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate 2>/dev/null \
  | awk -F'=' '{cmd="date -d \""$2"\" +%s"; cmd | getline d; close(cmd); print int((d-'"$(date +%s)"')/86400)}')

THRESHOLD=15
if [ "${DAYS_LEFT:-999}" -lt "$THRESHOLD" ]; then
  echo "ALERT: example.com 证书剩余 ${DAYS_LEFT} 天" | mail -s "SSL 证书即将到期" ops@example.com
fi
bash
# 加入 crontab,每天上午 9 点检查
0 9 * * * /opt/scripts/check-cert-expiry.sh >> /var/log/cert-check.log 2>&1

自动续期如果使用 Let's Encrypt(免费 DV 证书,有效期 90 天),certbot 是标准方案。截至 2026 年,certbot 官方推荐使用 snap 安装以获得最新版本:

bash
# 安装 certbot(Nginx 插件 + DNS 插件按需)
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# HTTP-01 方式签发(需要 80 端口对外可达)
sudo certbot certonly --nginx -d example.com -d www.example.com

# 通配符证书必须用 DNS-01 方式
sudo certbot certonly --manual --preferred-challenges dns \
  -d "*.example.com" -d example.com

# 测试续期(--dry-run 不会真的消耗签发配额)
sudo certbot renew --dry-run

# systemd timer 每 12 小时自动检查续期,并重载 Nginx
sudo systemctl enable --now snap.certbot.renew.timer
sudo certbot renew --deploy-hook "systemctl reload nginx"

Let's Encrypt 有几个硬性配额上限,规划时务必注意:同一注册域名每周 50 张证书、每域名每周 5 张重复证书、每小时 5 次失败验证。调 --dry-run 不会计入配额,但误用它做压力测试会触发限流。

常见误区 / 排错提示

  • 误区一:把私钥文件用 .crt 拷给同事。 私钥泄露等于证书可被伪造,必须重新签发(Revoke + Reissue)。/etc/ssl/private 目录权限应是 700,私钥文件 600。
  • 误区二:ssl_certificate 只配了域名证书没配中间证书。 桌面浏览器正常、部分客户端报错,就是这个原因。永远用 fullchain。
  • 误区三:证书生效后仍然重启 Nginx 而不是 reload。 reload 才能不中断存量连接地加载新证书;若执行 nginx -s reload 后证书仍没变,检查是否有旧 worker 进程未退出。
  • 误区四:一台服务器多个 HTTPS 站点只配了一个 default_server 的证书。 没配证书且被 SNI 命中的 server 块会回退到默认 SSL 站点,返回错误证书。每个 server 块都应该有独立的 ssl_certificate。
  • 误区五:Tomcat 里 keyAlias 与导出时 -name 不一致。 表现是启动日志报 Alias name [xxx] does not identify a key entry。用 keytool -list -v -keystore xxx.jks 确认别名。
  • 误区六:改了证书后浏览器还报旧证书。 是本地 SSL 会话缓存或 OCSP 缓存没清。用隐身窗口或 openssl s_client 绕过浏览器验证,不要反复重装。

常见问题(FAQ)

SSL 证书安装后浏览器还是提示不安全,是什么原因?

最常见的原因是证书链不完整或页面存在混合内容(Mixed Content)。 先用 openssl s_client -connect 域名:443 -servername 域名 检查握手是否正常、链是否返回 2~3 张证书;如果是锁标但显示"部分内容不安全",则是页面里有 http:// 的 js/css/图片资源,需要在 Nginx 加 add_header Content-Security-Policy "upgrade-insecure-requests"; 或把资源改成相对协议。

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

加密强度完全相同,差别在于有效期、验证等级、保险赔付与技术支持。 免费证书以 Let's Encrypt 为代表,DV 级、90 天有效期、需自动续期、无赔付;付费 OV/EV 提供企业身份核验、最长 13 个月有效期(截至 2026 年 CA/B 论坛规定)、数十万至数百万美元赔付和技术支持。企业对外业务建议 OV 起,内部系统与个人站用免费证书完全够。

一张证书可以在多台服务器上用吗?

可以,只要多个节点对外提供的是同一个域名。 把私钥与证书文件同步到所有节点即可,这对负载均衡集群是标准做法。需要注意两点:一是私钥分发要走加密通道(如 rsync over SSH、Vault),二是不要在宝塔之类的面板里重复申请同一域名的新证书,容易触发 CA 的重复证书配额限制。

通配符证书能把根域名也保护了吗?

**默认情况下不能,必须在 SAN 里显式同时列出 *.example.com 和 example.com。** 原因是通配符只在证书的 subjectAltName 里声明了 .example.com 这一个条目,而浏览器自 2018 年起完全按 SAN 匹配域名,裸域名不在列表里就会被判为证书不匹配。另外通配符只匹配一级子域名,a.b.example.com 这种二级子域名不会被 .example.com 覆盖,需要单独添加 SAN 条目或再申请一张 *.b.example.com 证书。申请时的判断方法很简单:把所有要保护的域名逐条写进 SAN,签发后用 openssl x509 -noout -text | grep -A2 "Subject Alternative Name" 核对一遍再部署。

证书到期没续会怎样?怎么防止忘记?

到期后所有访问该站点的浏览器会弹出红色警告页,用户需手动点击"继续"才能访问,实际等同于业务中断。 防御手段有三层:一是 certbot + systemd timer 自动续期;二是部署上述巡检脚本并在剩余 15 天时告警;三是购买支持的平台可用 SSL 证书,把证书基本信息(含到期时间)登记到台账或 CMDB 中统一管理。

用 CDN 时证书该装在哪一层?

两处都要装:CDN 上装面向用户的证书(回调必须是 https),源站也要装证书(可以是自签名或 CDN 厂商提供的源站证书)。 如果 CDN 与源站之间配置了严格校验,则源站必须用有效公开信任的证书,否则回源握手失败返回 5xx。用 CDN 厂商的一键托管证书(如腾讯云 SSL、阿里云证书中心托管)可以避免手动轮换。