Appearance
HTTPS 证书
为什么要自己配置
各大云厂商(阿里云、腾讯云等)提供的免费证书有效期只有 90 天,到期后需要手动登录控制台 → 重新申请 → 下载 → 上传到服务器 → 重启 Nginx,流程繁琐且容易忘记。
Let's Encrypt 是一个免费、自动化的证书颁发机构(CA),配合 Certbot 工具,可以实现:
- 一行命令申请证书
- 系统定时任务自动续签(到期前 30 天自动处理)
- 续签后自动重载 Nginx
配好一次后完全无需人工介入。
前提条件:
- 已有一个域名,并将 DNS A 记录解析到服务器 IP
- 云控制台安全组已放行 80(HTTP)和 443(HTTPS)端口
安装 Certbot
先确认当前系统,再按对应步骤操作:
bash
cat /etc/os-release | grep -E "^(NAME|VERSION_ID|ID)"根据输出判断系统:
| 输出关键字 | 对应系统 | 使用哪个安装方式 |
|---|---|---|
NAME="CentOS Linux" / AlmaLinux | CentOS / AlmaLinux(RHEL 8 系) | 👇 方式一 |
NAME="Alibaba Cloud Linux" / NAME="Anolis OS" | 阿里云 Linux / Anolis | 👇 方式二 |
NAME="Ubuntu" / NAME="Debian" | Ubuntu / Debian | 👇 方式三 |
不确定时再看 Python 版本,Python 3.9 = RHEL 8 系,Python 3.11 = RHEL 9 系(即方式二):
bash
python3 --versionCentOS / AlmaLinux(RHEL 8 系):
bash
# 1. 安装 EPEL 源
yum install epel-release -y
# 2. 安装 Certbot 和 Nginx 插件
yum install certbot python3-certbot-nginx -y
# 3. 验证
certbot --versionAlibaba Cloud Linux 4 / Anolis OS(RHEL 9 系):
该系统自带 Python 3.11,而 EPEL 仓库的 Certbot 依赖 Python 3.9,直接 dnf install 会报依赖冲突。需要用 pip 安装:
bash
# 1. 安装 pip
dnf install python3-pip -y
# 2. 通过 pip 安装 Certbot 和 Nginx 插件
pip3 install certbot certbot-nginx
# 3. 验证
certbot --version如果 pip 安装后
certbot命令找不到,运行ln -s /usr/local/bin/certbot /usr/bin/certbot创建软链接
Ubuntu / Debian:
bash
# 默认仓库已包含,直接安装
apt install certbot python3-certbot-nginx -y
# 验证
certbot --version备选方案:snap 安装(适用于所有 Linux)
如果上述方式都遇到问题,snap 安装完全独立于系统 Python,不会有依赖冲突:
bash
# 1. 安装 snapd
dnf install snapd -y # CentOS / RHEL 系
apt install snapd -y # Ubuntu / Debian
# 2. 启用 snap
systemctl enable --now snapd.socket
ln -s /var/lib/snapd/snap /snap
# 3. 安装 Certbot
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
# 4. 验证
certbot --versionDocker Nginx 配置流程
Nginx 跑在容器里时,不能直接使用 --nginx 插件(插件需要在宿主机上找到并修改 nginx.conf),需要在宿主机上运行 Certbot,借用容器内的 Nginx 完成 HTTP-01 验证。
--webroot 是什么:Certbot 不自己启动任何服务,而是把临时验证文件写入 -w 指定的目录,由已在运行的 Nginx 直接提供该文件的 HTTP 访问。Let's Encrypt 通过访问 http://your-domain/.well-known/acme-challenge/ 完成验证,整个过程 Nginx 不中断。"手动"指的是需要你提前在 Nginx 配置中写好 /.well-known/acme-challenge/ 的路由。
第一步:配置 docker-compose.yml
将证书目录和 webroot 验证目录挂载进 Nginx 容器:
yaml
services:
nginx:
image: nginx:alpine
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d # Nginx 配置
- /etc/letsencrypt:/etc/letsencrypt:ro # 证书目录(只读)
- /var/www/certbot:/var/www/certbot # webroot 验证目录
restart: unless-stopped第二步:配置 Nginx 允许 webroot 验证
在申请证书之前,Nginx 配置中只写 HTTP,还不能写 HTTPS(证书文件尚不存在,Nginx 会启动失败):
nginx
server {
listen 80;
server_name example.com www.example.com;
# Let's Encrypt webroot 验证路径(必须保留)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 其他请求暂时正常响应即可,申请到证书后再改为跳转 HTTPS
location / {
return 200 "ok";
}
}bash
# 创建 webroot 目录并启动容器
mkdir -p /var/www/certbot
docker compose up -d第三步:申请证书
宿主机上运行 Certbot,使用 webroot 模式借用 Nginx 容器完成验证:
bash
certbot certonly --webroot -w /var/www/certbot -d example.com -d www.example.com
-w只控制临时验证文件的写入位置,验证完成后自动删除。证书始终保存在/etc/letsencrypt/live/example.com/,与-w无关。容器通过卷挂载读取该目录。
第四步:更新 Nginx 配置启用 HTTPS
证书申请成功后,补充 HTTPS 配置(路径与宿主机直装 Nginx 完全相同,因为目录已挂载进容器):
nginx
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name example.com www.example.com;
# 路径是容器内的挂载路径,与宿主机一致
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
root /usr/share/nginx/html;
index index.html;
}bash
# 重载容器内 Nginx(不重启容器,不中断连接)
docker compose exec nginx nginx -s reload第五步:配置续签 reload 钩子
deploy 钩子改为操作容器,注意要指定 docker compose 所在的项目目录:
bash
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx-docker.sh << 'EOF'
#!/bin/bash
# 改为你的 docker-compose.yml 实际路径
cd /path/to/your/project
docker compose exec nginx nginx -s reload
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx-docker.sh最后验证自动续签是否正常:
bash
certbot renew --dry-run配置自动续签
通过系统包管理器(yum/apt)安装的 Certbot 会自动注册 systemd timer:
bash
# 确认 timer 已启用
systemctl status certbot.timer
# 若未启用,手动开启
systemctl enable --now certbot.timer
# 查看下次执行时间
systemctl list-timers | grep certbot通过 snap 安装的 Certbot,timer 名称不同,同样自动注册无需额外配置:
bash
systemctl status snap.certbot.renew.timer通过 pip 安装的 Certbot 不会注册 systemd timer,需要手动配置 cron。
先确认 certbot 的实际路径:
bash
which certbot然后添加 cron 任务:
bash
crontab -e # 编辑 cron 任务列表加入以下内容(使用 which certbot 得到的完整路径,避免 cron 环境找不到命令):
0 2 * * * /usr/bin/certbot renew --quiet常用 crontab 参数:
| 参数 | 含义 |
|---|---|
crontab -l | 查看当前 cron 任务列表 |
crontab -e | 编辑 cron 任务 |
crontab -r | 删除所有 cron 任务(慎用) |
验证续签配置正常(不会真正续签):
bash
certbot renew --dry-run续签钩子目录说明
| 钩子目录 | 执行时机 | 用途 |
|---|---|---|
pre/ | 续签之前 | 预处理(如备份) |
deploy/ | 续签成功之后 | 重载 Nginx 使新证书生效(Docker 场景必须配) |
post/ | 续签完成后(无论成败) | 清理操作 |
DNS-01 验证模式(通配符证书)
HTTP-01 只能给具体域名签证书(example.com、www.example.com),不支持通配符。如果想用 *.example.com 覆盖所有子域名,需要用 DNS-01 验证。
原理:Certbot 要求你在 DNS 中添加一条 TXT 记录,Let's Encrypt 通过查询 DNS 来验证域名所有权。
bash
# 手动 DNS 验证方式(需要手动去 DNS 控制台添加 TXT 记录)
certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d example.com执行后会提示:
Please deploy a DNS TXT record under the name:
_acme-challenge.example.com
with the following value:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx去域名的 DNS 管理后台(阿里云 → 域名解析 → 添加记录):
| 记录类型 | 主机记录 | 记录值 |
|---|---|---|
| TXT | _acme-challenge | Certbot 提示的那串值 |
添加后等 1~2 分钟 DNS 生效,回到终端按回车继续。
缺点:手动模式下每次续签都要手动改 DNS 记录,无法自动化。如果你的 DNS 服务商支持 API(如阿里云、Cloudflare),可以用 acme.sh 实现全自动。
acme.sh(轻量替代方案)
acme.sh 是纯 Shell 实现的 ACME 客户端,比 Certbot 更轻量,且原生支持各大 DNS 服务商 API,是通配符证书自动续签的最佳选择。
安装
bash
curl https://get.acme.sh | sh -s email=your@email.com
# 使脚本生效(或重新登录终端)
source ~/.bashrc使用 DNS API 申请通配符证书(以阿里云为例)
先在阿里云创建 AccessKey(RAM 用户 → 创建用户 → 赋予 DNS 权限 → 获取 Key),然后:
bash
# 设置阿里云 DNS API 密钥
export Ali_Key="你的AccessKeyId"
export Ali_Secret="你的AccessKeySecret"
# 申请通配符证书(acme.sh 会自动添加和删除 DNS TXT 记录)
acme.sh --issue --dns dns_ali -d example.com -d "*.example.com"安装证书到 Nginx(Docker 版)
bash
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.pem \
--reloadcmd "docker compose -f /path/to/your/project/docker-compose.yml exec nginx nginx -s reload"bash
# 创建证书目录(宿主机上)
mkdir -p /etc/nginx/ssl在 Nginx 配置中引用:
nginx
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;acme.sh 会自动注册 cron 定时任务,到期前自动续签并执行 --reloadcmd:
bash
# 查看定时任务
crontab -l | grep acme.sh
# 手动测试续签
acme.sh --renew -d example.com --forceCertbot vs acme.sh
| 对比项 | Certbot | acme.sh |
|---|---|---|
| 安装方式 | 系统包管理器 | 一行 curl 脚本 |
| 依赖 | Python 运行时 | 纯 Shell,无额外依赖 |
| Nginx 集成 | 官方插件,自动改配置 | 手动指定证书路径 |
| 通配符证书 | 需要手动 DNS 验证 | 原生支持 DNS API,全自动 |
| 自动续签 | systemd timer | cron 定时任务 |
| 推荐场景 | 单域名 + Nginx 环境 | 通配符证书 / 多域名 / 无 Nginx |
添加新子域名
配好初始证书后,如果需要给新子域名(如 api.example.com)也启用 HTTPS,不能复用现有证书文件——Let's Encrypt 签发的证书绑定了申请时指定的域名列表,新增域名需要重新向 Let's Encrypt 验证所有权、更新证书。
前提:新子域名的 DNS A 记录已解析到服务器 IP。
第一步: 重新申请证书,列出所有域名(含新增的)
bash
certbot certonly --webroot -w /var/www/certbot \
-d example.com \
-d www.example.com \
-d api.example.com # 新增的子域名Certbot 检测到已有证书会询问是否扩展,选择
Expand
第二步: 在 Nginx 配置中为新子域名添加 server 块
nginx
server {
listen 80;
server_name api.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name api.example.com;
# 证书与主域名共用同一份(扩展后已包含新子域名)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 按需配置:反向代理到后端服务
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}第三步: reload 容器内 Nginx
bash
docker compose exec nginx nginx -s reload子域名多时的最佳实践
| 情况 | 推荐方案 |
|---|---|
| 子域名固定、数量少(≤ 5 个) | 单独申请,逐条 -d 追加 |
| 子域名多或经常新增 | 申请 *.example.com 通配符证书,新增子域名无需重签 |
通配符证书参考 DNS-01 验证模式 章节,一次覆盖所有子域名,新增子域名只需配置 Nginx,无需重新申请证书。
常用管理命令
bash
# === Certbot ===
certbot certificates # 查看所有证书及到期时间
certbot renew # 续签所有临近到期的证书
certbot renew --dry-run # 模拟续签(验证配置是否正常)
certbot renew --cert-name example.com # 续签指定域名
certbot delete --cert-name example.com # 删除证书
journalctl -u certbot # 查看续签日志
# === acme.sh ===
acme.sh --list # 查看所有证书及到期时间
acme.sh --renew -d example.com # 续签指定域名
acme.sh --renew -d example.com --force # 强制续签(测试用)
acme.sh --remove -d example.com # 删除证书证书吊销
证书吊销用于在证书泄露或不再需要时主动使其失效,Let's Encrypt 会将其加入吊销列表,浏览器访问时会报错。续签失败或迁移服务器时不需要吊销,直接续签或申请新证书即可。
bash
# 吊销证书(需指定证书文件路径)
certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem
# 吊销后删除本地证书文件(可选)
certbot delete --cert-name example.com证书吊销后无法恢复,需重新申请。如果只是不再使用该域名,直接
certbot delete删除本地文件即可,不必吊销。