Let's Encrypt 证书申请
一、概述
什么是 Let's Encrypt
Let's Encrypt 是一个由 Internet Security Research Group (ISRG) 运营的非营利性证书颁发机构 (CA),致力于降低 HTTPS 部署的门槛。
核心特点:
- 免费:无需支付任何费用即可获取 SSL/TLS 证书
- 自动化:通过 Certbot 工具实现证书的申请、续期全自动化
- 开放标准:基于 ACME (Automated Certificate Management Environment) 协议
- 兼容性好:被所有主流浏览器和操作系统信任
证书有效期
Let's Encrypt 签发的证书有效期为 90 天,到期前 30 天内可以续期。官方推荐使用自动续期机制,无需手动干预。
支持的验证方式
| 验证方式 | 说明 | 适用场景 |
|---|---|---|
| webroot | 在网站根目录放置验证文件,通过 HTTP 访问验证 | 已有 Web 服务器运行且 80 端口可访问 |
| standalone | 临时启动独立 Web 服务器进行验证 | 服务器上没有 Web 服务,或 80/443 端口空闲 |
| nginx/apache | 自动修改 Nginx/Apache 配置进行验证 | 使用 Nginx 或 Apache 作为 Web 服务器 |
| DNS-01 | 在 DNS 解析中添加 TXT 记录验证域名所有权 | 申请通配符证书(*.example.com)或 80 端口不可用 |
二、前置条件
在开始配置 SSL 证书之前,请确认已满足以下条件:
1. 域名准备
- 已注册可用的域名(如
example.com) - 域名 DNS 解析已正确配置,A 记录 指向服务器的公网 IP 地址
- 可以使用以下命令验证域名解析:
# 检查域名解析是否生效
dig +short example.com
# 或
nslookup example.com
ping example.com2. 服务器准备
- 拥有服务器的 root 或 sudo 权限
- 服务器可以访问公网(能连通 Let's Encrypt 服务器)
- 操作系统:本文档以 CentOS/RHEL 为例,其他发行版命令略有差异
3. 端口检查
确保 80 和 443 端口未被占用且防火墙放行:
# 检查端口占用情况
netstat -tlnp | grep -E ':(80|443)\s'
# 或
ss -tlnp | grep -E ':(80|443)\s'三、放开端口
HTTPS 服务需要 80(HTTP,用于证书验证和跳转)和 443(HTTPS)端口开放。
查看当前防火墙规则
firewall-cmd --list-all开放 80 和 443 端口
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --reload说明:
--permanent:将规则永久写入配置,重启后不丢失--reload:重新加载防火墙规则,使配置立即生效如果使用的是 iptables 而非 firewalld,对应命令:
iptables -I INPUT -p tcp --dport 80 -j ACCEPT iptables -I INPUT -p tcp --dport 443 -j ACCEPT service iptables save如果服务器在云平台(阿里云/腾讯云/AWS 等),还需要在 云平台安全组 中放行相应端口。
四、安装 Certbot
Certbot 是 EFF (Electronic Frontier Foundation) 开发的 ACME 协议客户端工具,用于自动化申请和管理 Let's Encrypt 证书。
# CentOS/RHEL
yum install epel-release
yum install certbot说明:
epel-release:EPEL (Extra Packages for Enterprise Linux) 仓库,Certbot 包位于此仓库中- Ubuntu/Debian 系统安装命令:
apt-get install certbot- 安装完成后验证:
certbot --version
五、申请证书
webroot 方式
该方式通过在网站根目录下创建临时验证文件,Let's Encrypt 服务器通过 HTTP 访问该文件来验证域名所有权。
前提:80 端口可以正常访问,且网站根目录可写。
/var/www/html 替换为你的网站资源所在的目录
# 指定网站根目录
certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com参数说明:
certonly:只申请证书,不自动配置 Web 服务器--webroot:使用 webroot 插件进行验证-w /var/www/html:指定网站根目录(Web 服务器的 document root)-d example.com:指定要签发证书的域名(支持多个-d参数一次性签发多域名/SAN 证书)首次执行时会提示:
- 输入邮箱(用于接收证书到期提醒和安全通知)
- 同意服务条款 (A)
- 是否同意接收 EFF 邮件(Y/N 自选)
签发成功后会看到
Congratulations! Your certificate and chain have been saved at...的提示。
其他申请方式简介
standalone 方式
适用于服务器上没有运行 Web 服务(80/443 端口空闲)的场景,Certbot 会临时启动一个 Web 服务器完成验证:
# 先停止占用 80 端口的服务(如 nginx)
systemctl stop nginx
# 以 standalone 方式申请
certbot certonly --standalone -d example.com -d www.example.com
# 重新启动 Web 服务
systemctl start nginxnginx 插件方式
Certbot 会自动修改 Nginx 配置完成验证,无需手动指定网站根目录:
certbot certonly --nginx -d example.com -d www.example.com通配符证书(DNS 验证)
申请 *.example.com 格式的通配符证书必须使用 DNS-01 验证,需要在域名 DNS 中添加 TXT 记录:
certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d example.com执行后按照提示在 DNS 管理后台添加指定的 TXT 记录,验证通过后即可获得通配符证书。
六、证书文件说明
证书存放在 /etc/letsencrypt/live/example.com/:
| 文件 | 说明 | 用途 |
|---|---|---|
| cert.pem | 域名证书 | 一般不直接使用 |
| chain.pem | 中间证书链 | 用于构建信任链 |
| fullchain.pem | 完整证书链 | Nginx ssl_certificate |
| privkey.pem | 私钥 | Nginx ssl_certificate_key |
重要提示:
/etc/letsencrypt/live/目录下的文件是符号链接,实际文件存储在/etc/letsencrypt/archive/目录中- 证书续期后,符号链接会自动指向最新的证书文件,因此 Nginx 配置中务必使用 live 目录下的路径
- 私钥文件 (privkey.pem) 必须严格保密,权限应为 600,绝对不能泄露或提交到代码仓库
- 可以使用命令
ls -la /etc/letsencrypt/live/example.com/查看符号链接指向
七、Nginx 配置 HTTPS
完整配置示例
# HTTP → HTTPS 跳转
server {
listen 80;
server_name www.example.com example.com;
return 301 https://$server_name$request_uri;
}
# HTTPS 主站
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
# ssl_dhparam /etc/ssl/private/dhparam.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK';
ssl_prefer_server_ciphers on;
# 网站静态资源路径
root /usr/local/;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}配置项说明
| 配置项 | 说明 |
|---|---|
listen 443 ssl | 监听 443 端口并启用 SSL |
ssl_certificate | 指向完整证书链文件 (fullchain.pem) |
ssl_certificate_key | 指向证书私钥文件 (privkey.pem) |
ssl_session_cache | SSL 会话缓存,1m 约可存储 4000 个会话,减少 TLS 握手开销 |
ssl_session_timeout | 会话缓存有效期 5 分钟 |
ssl_protocols | 允许的 TLS 协议版本(建议至少 TLSv1.2+) |
ssl_ciphers | 允许的加密套件列表,优先使用前向安全 (PFS) 的 ECDHE 套件 |
ssl_prefer_server_ciphers on | 优先使用服务器端的加密套件顺序 |
配置验证与生效
# 检查 Nginx 配置语法是否正确
nginx -t
# 重新加载配置(不中断服务)
nginx -s reload
# 或
systemctl reload nginx验证通过后,使用浏览器访问 https://your-domain.com 即可看到地址栏的安全锁图标。
八、证书自动续期
Let's Encrypt 证书有效期仅 90 天,必须配置自动续期避免证书过期导致网站无法访问。
Certbot 续期原理
Certbot 续期命令 (certbot renew) 会检查 /etc/letsencrypt/renewal/ 目录下的所有证书配置,对有效期不足 30 天的证书自动执行续期操作。
创建 certbot.service 文件
这个文件定义了续期时要执行的命令,是 systemd 的服务单元文件。
sudo tee /etc/systemd/system/certbot.service > /dev/null << 'EOF'
[Unit]
Description=Certbot Renewal
Documentation=https://certbot.eff.org/docs
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true
EOFType=oneshot:表示这是一个一次性任务,执行完即退出ExecStart=/usr/bin/certbot -q renew:核心命令,-q参数表示静默执行,仅输出错误信息After=network-online.target:确保网络完全就绪后再执行PrivateTmp=true:使用独立的临时目录,增强安全性
创建 certbot.timer 文件
这个文件定义了什么时候触发上面的 certbot.service,是 systemd 的定时器单元。
sudo tee /etc/systemd/system/certbot.timer > /dev/null << 'EOF'
[Unit]
Description=Run certbot twice daily
Requires=certbot.service
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=12h
Persistent=true
[Install]
WantedBy=timers.target
EOFOnCalendar=*-*-* 00,12:00:00:设定在每天 0 点和 12 点触发。RandomizedDelaySec=12h:增加一个最长 12 小时的随机延迟,避免所有服务器在同一时间访问 Let's Encrypt 服务器,降低对 CA 服务器的压力。Persistent=true:如果服务器在预定时间关机,下次开机时会立即执行错过的续期任务。WantedBy=timers.target:加入系统定时器目标,开机自动启用。
启用并启动定时器
文件创建好后,执行以下命令来启用和启动定时器:
# 重新加载systemd配置
sudo systemctl daemon-reload
# 启用定时器(开机自启)
sudo systemctl enable certbot.timer
# 立即启动定时器
sudo systemctl start certbot.timer验证自动续期配置
# 检查定时器状态
sudo systemctl status certbot.timer
# 查看定时器列表,确认下次执行时间
sudo systemctl list-timers | grep certbot
# 测试自动续期
sudo certbot renew --dry-run如果看到类似 Congratulations, all simulated renewals succeeded 的信息,就说明配置成功了。
关于
--dry-run:
- 使用 Let's Encrypt 的测试环境(Staging Environment)模拟续期流程
- 不会真正签发或续期证书
- 不会触发速率限制,可反复测试
- 建议在任何修改续期配置后都执行此命令验证
九、续期后自动重载 Nginx
为什么需要自动重载
证书续期成功后,新证书文件会更新到 /etc/letsencrypt/archive/ 目录,live 目录的符号链接也会自动指向新文件。但是 Nginx 不会自动感知证书变化,必须 reload 才能加载新证书。
如果没有自动重载,Nginx 会继续使用内存中已加载的旧证书,即使文件已更新,用户访问时仍会提示证书过期。
配置部署钩子
证书续期后,需要让 Nginx 重新加载以使用新证书。可以创建一个部署钩子(deploy hook)来实现。
创建脚本文件:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh > /dev/null << 'EOF' #!/bin/sh systemctl reload nginx EOF赋予执行权限:
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
这样,每次证书成功续期后,Nginx 都会自动重载。
钩子类型说明
Certbot 支持三种类型的钩子,按执行时机区分:
| 钩子目录 | 执行时机 | 典型用途 |
|---|---|---|
/etc/letsencrypt/renewal-hooks/pre/ | 续期操作开始前执行 | 临时停止某些服务、创建 DNS 记录前的准备等 |
/etc/letsencrypt/renewal-hooks/deploy/ | 证书成功续期并部署后执行 | 重载 Nginx/Apache、同步证书到其他服务器、清理缓存等 |
/etc/letsencrypt/renewal-hooks/post/ | 续期操作结束后(无论成功失败)执行 | 恢复停止的服务、发送通知、记录日志等 |
重启nginx钩子原理
- 当程序执行成功续期时,代码流程会进入一个
post-success(成功后)逻辑分支。 - 在这个分支中,程序会执行一个特定的函数。
- 这个函数会去检查预定义的位置(硬编码或基于配置的路径)。
- 具体位置是:
a) 命令行参数(--deploy-hook)。
b) 特定于域的配置文件(/etc/letsencrypt/renewal/example.com.conf)中的deploy_hook/renew_hook值。
c) 扫描/etc/letsencrypt/renewal-hooks/deploy/目录,以查找 可执行文件。
十、证书管理常用命令
查看已申请的证书
certbot certificates该命令会列出所有已申请的证书,包括:
- 证书路径
- 域名列表
- 有效期
- 到期时间
手动续期证书
# 续期所有即将到期的证书
certbot renew
# 强制续期(忽略 30 天限制,测试时使用)
certbot renew --force-renewal注意:正常情况下请不要频繁使用
--force-renewal,会触发 Let's Encrypt 的速率限制。
撤销证书
如果私钥泄露或域名不再使用,应及时撤销证书:
certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem删除证书
删除不再需要的证书(会同时删除续期配置):
certbot delete执行后会列出所有证书,选择编号确认删除。
更新 Certbot 配置
修改证书关联的域名(添加/删除域名):
certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com -d new.example.com --expand--expand 参数表示扩展现有证书,保留原证书的其他配置。
十一、安全增强配置
启用 HSTS(HTTP Strict Transport Security)
HSTS 强制浏览器只能通过 HTTPS 访问网站,防止降级攻击和 Cookie 劫持。
在 Nginx 的 HTTPS server 块中添加:
# 启用 HSTS,有效期 1 年,包含子域名
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;参数说明:
max-age=31536000:浏览器记住 HTTPS 强制策略的时间(秒),一年includeSubDomains:对所有子域名也生效(谨慎使用,确保所有子域名都支持 HTTPS)always:确保在错误响应(如 4xx/5xx)中也返回此头注意:首次配置建议将
max-age设置较小值(如 3600)测试,确认无问题后再加大。
生成和启用 DH 参数
增强前向安全性 (PFS),防止 TLS 会话被事后解密:
# 生成 2048 位 DH 参数(耗时较长,可能需要几分钟)
openssl dhparam -out /etc/ssl/private/dhparam.pem 2048在 Nginx 配置中启用(取消注释原配置中的对应行):
ssl_dhparam /etc/ssl/private/dhparam.pem;启用 OCSP Stapling
OCSP Stapling 将证书吊销检查的 OCSP 响应由服务器代为查询并缓存,减少客户端握手时间,保护用户隐私。
在 Nginx 配置中添加:
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;现代 TLS 配置(推荐)
根据安全性和兼容性需求调整协议和加密套件:
# 仅启用现代协议(兼容性:现代浏览器,IE11/Android 4.x 不支持)
ssl_protocols TLSv1.2 TLSv1.3;
# 推荐的加密套件(去除老旧不安全算法)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';其他安全头
# 防止点击劫持
add_header X-Frame-Options "SAMEORIGIN" always;
# 防止 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# XSS 保护(现代浏览器已默认启用)
add_header X-XSS-Protection "1; mode=block" always;
# Referrer 策略
add_header Referrer-Policy "strict-origin-when-cross-origin" always;十二、常见问题排查
1. 端口 80/443 被占用导致申请失败
错误提示:Problem binding to port 80: Could not bind to IPv4 or IPv6.
解决方法:
# 查找占用端口的进程
netstat -tlnp | grep ':80'
# 或
lsof -i :80
# 临时停止占用端口的服务
systemctl stop nginx
# 或
systemctl stop httpd
# 申请完成后重新启动
systemctl start nginx2. 域名解析验证失败
错误提示:Failed authorization procedure. example.com (http-01): urn:acme:error:unauthorized
排查步骤:
# 1. 确认域名解析指向正确的 IP
dig +short example.com
# 或访问 https://dnschecker.org 检查全球 DNS 传播
# 2. 手动测试验证文件访问是否正常
# 假设网站根目录为 /var/www/html
mkdir -p /var/www/html/.well-known/acme-challenge/
echo "test-content" > /var/www/html/.well-known/acme-challenge/test
# 然后通过浏览器或 curl 访问:
curl http://example.com/.well-known/acme-challenge/test
# 如果返回 "test-content" 说明正常
# 3. 检查 Nginx 是否有 location 拦截了 /.well-known/ 路径3. 触发速率限制
错误提示:Error creating new cert :: too many certificates already issued for exact set of domains
Let's Encrypt 对每个域名有速率限制(默认:同一主域名每周最多签发 50 个相同域名集的证书)。
解决方法:
- 测试环境使用
--dry-run或--test-cert参数 - 等待限制窗口过去(7 天)
- 如果是多域名证书,可以调整域名顺序或组合
4. Nginx 配置 HTTPS 后访问提示证书不匹配
排查步骤:
# 查看证书中的域名
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -text | grep -A1 "Subject Alternative Name"
# 检查 Nginx 的 server_name 是否与证书域名匹配
# 检查 ssl_certificate 路径是否正确5. 证书自动续期不工作
排查步骤:
# 1. 检查定时器是否运行
systemctl status certbot.timer
# 2. 检查 Certbot 服务日志
journalctl -u certbot.service -n 50
# 3. 手动测试续期
certbot renew --dry-run -v
# 4. 检查部署钩子是否有执行权限
ls -la /etc/letsencrypt/renewal-hooks/deploy/
# 5. 检查 certbot 路径是否正确(不同安装方式路径可能不同)
which certbot
# 路径必须与 certbot.service 中的 ExecStart 一致6. 浏览器提示混合内容(Mixed Content)
HTTPS 页面中引用了 HTTP 资源(图片、CSS、JS 等),浏览器会阻止加载。
解决方法:
- 将页面中所有
http://链接改为https://或使用协议相对路径// - 检查 Nginx 中
proxy_set_header是否正确设置了X-Forwarded-Proto
十三、SSL 配置检测工具
配置完成后,建议使用以下工具进行全面的 SSL 安全性检测:
| 工具名称 | 地址 | 说明 |
|---|---|---|
| SSL Labs Server Test | https://www.ssllabs.com/ssltest/ | 业界最权威的 SSL 检测工具,评分 A+ ~ F |
| SSL Checker | https://www.sslshopper.com/ssl-checker.html | 快速检测证书链、有效期、域名匹配 |
| Mozilla SSL Config Generator | https://ssl-config.mozilla.org/ | 根据你的服务端软件版本自动生成推荐的安全配置 |
| HSTS Preload Check | https://hstspreload.org/ | 检测并提交域名到浏览器 HSTS 预加载列表 |
| testssl.sh | 命令行本地检测 | `curl -sL https://testssl.sh |