一、Let’s Encrypt 是什么

Let’s Encrypt 是一个免费、自动化、开放的证书颁发机构(CA),由 ISRG(Internet Security Research Group)运营。它的使命是让每个网站都能用上 HTTPS。

特点 说明
免费 完全免费,无任何收费项
自动化 通过 ACME 协议自动申请、验证、部署、续期
安全 和付费证书同等级别,浏览器信任链完整
短期 证书有效期 90 天,强迫自动化续期

和传统付费证书的区别:

Let’s Encrypt 传统付费证书
价格 免费 $10-$1000+/年
有效期 90 天 1 年
通配符 支持(DNS 验证) 部分支持
OV/EV 不支持(仅 DV) 支持
申请方式 命令行自动化 网页提交 + 人工审核
续期 自动 手动/付费自动

说明:DV(域名验证)只验证域名所有权,OV/EV 还需要验证组织身份。个人博客、中小型网站用 DV 完全足够。

二、核心原理:ACME 协议

ACME(Automatic Certificate Management Environment)是 Let’s Encrypt 使用的自动化证书管理协议。

工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
你的服务器                    Let's Encrypt CA
│ │
│ 1. 发送证书请求 │
│ (包含域名列表) │
│ ──────────────────────────▶ │
│ │
│ 2. 返回挑战(Challenge) │
│ (验证域名所有权) │
│ ◀────────────────────────── │
│ │
│ 3. 完成挑战 │
│ ──────────────────────────▶ │
│ │
│ 4. 验证通过,签发证书 │
│ ◀────────────────────────── │

两种验证方式

验证方式 原理 适用场景
HTTP-01 CA 访问 http://你的域名/.well-known/acme-challenge/token,验证文件存在 公网可访问的服务器
DNS-01 你在 DNS 添加一条 TXT 记录,CA 查询 DNS 验证 通配符证书、内网服务器、CDN 后面

两种方式的对比:

HTTP-01 DNS-01
网络要求 80 端口公网可达 只要能改 DNS 记录
通配符证书 不支持 支持
自动化难度 简单 依赖 DNS 服务商 API
适用 一般 Web 服务器 所有场景

三、客户端工具选择

ACME 客户端不止 Certbot 一个,主流选择:

客户端 特点 推荐场景
Certbot Let’s Encrypt 官方推荐,功能全面 新手、标准 Nginx/Apache 场景
acme.sh 纯 Shell,零依赖,DNS API 丰富 高级用户、需要 DNS 验证、国内云服务商
lego Go 编写,单二进制文件 CI/CD、Docker 环境
Caddy Web 服务器自带自动 HTTPS Caddy 用户、最简单的方案

以下分别介绍 Certbot 和 acme.sh 两种主流方案。

四、Certbot 实战

安装

Ubuntu/Debian:

1
2
sudo apt update
sudo apt install certbot python3-certbot-nginx -y

CentOS/RHEL:

1
sudo dnf install certbot python3-certbot-nginx -y

检查版本:

1
certbot --version

参数速查

Certbot 的命令结构是 certbot [子命令] [参数]

子命令

子命令 说明
run 默认子命令,获取证书并安装(可省略不写)
certonly 仅获取证书,不修改 Web 服务器配置
renew 续期所有或指定证书
certificates 列出已获取的证书
revoke 吊销证书
delete 删除证书

常用参数

参数 说明 示例
--nginx 用 Nginx 插件自动获取并配置证书 certbot --nginx
--apache 用 Apache 插件自动获取并配置证书 certbot --apache
--webroot 在网站根目录放置验证文件来验证 certbot certonly --webroot -w /var/www/html
--standalone 自启一个临时 Web 服务器来验证(需要 80 端口空闲) certbot certonly --standalone
--manual 手动验证模式(交互式,用于非标场景) certbot certonly --manual
--dns-cloudflare 通过 Cloudflare DNS API 自动验证 certbot certonly --dns-cloudflare
-d 指定域名(可重复使用以包含多个域名) -d example.com -d www.example.com
-w 配合 --webroot,指定网站根目录 -w /var/www/html
--staging 使用测试环境(不触发速率限制) certbot --staging ...
--dry-run 试运行,不真正申请证书 certbot renew --dry-run
--force-renewal 强制续期,忽略到期时间检查 certbot renew --force-renewal
--preferred-challenges 指定验证方式(http / dns) --preferred-challenges http
--email 设置通知邮箱(到期提醒、紧急通知) --email admin@example.com
--agree-tos 同意服务条款(非交互模式用) --agree-tos
--non-interactive 非交互模式,不提问 --non-interactive
--nginx-server-root Nginx 配置目录(非标准路径时用) --nginx-server-root /opt/nginx/conf
--deploy-hook 续期成功后执行的命令 --deploy-hook "nginx -s reload"

验证方式对比

方式 参数 适用场景 要求
Nginx 插件 --nginx 标准 Nginx 场景 80 端口公网可达
Webroot --webroot -w /path 已有 Web 服务器 站点根目录可写
Standalone --standalone 临时操作,无 Web 服务器 80 端口空闲
DNS 插件 --dns-cloudflare 通配符证书、内网 DNS API 凭证
Manual --manual 非标场景 手动操作

方式一:自动配置(Nginx)

Certbot 会直接读取 Nginx 配置,找到匹配的域名,自动修改配置、添加 SSL:

1
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

按提示输入邮箱并同意条款,Certbot 会自动完成所有配置,然后重载 Nginx。

方式二:仅获取证书(手动配置)

如果你不想让 Certbot 改 Nginx 配置,用 certonly 模式:

1
2
3
4
sudo certbot certonly --manual \
--preferred-challenges dns \
-d your-domain.com \
-d *.your-domain.com

运行后 Certbot 会提示你在 DNS 后台添加一条 TXT 记录:

1
_acme-challenge.your-domain.com  →  TXT  →  "随机生成的验证字符串"

添加完按回车,验证通过后证书就到手了。

注意--manual 模式不支持自动续期,因为每次续期都需要手动添加 DNS 记录。生产环境建议用 --dns-cloudflare 等 DNS API 插件实现自动化。

获取后证书文件在 /etc/letsencrypt/live/your-domain.com/

1
2
3
4
5
/etc/letsencrypt/live/your-domain.com/
├── fullchain.pem # 完整证书链(server 用这个)
├── privkey.pem # 私钥
├── cert.pem # 域名证书
└── chain.pem # 中间证书

然后手动写 Nginx 配置引用证书即可。

配置 Nginx 使用证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {
listen 443 ssl http2;
server_name your-domain.com;

ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

# 推荐的 SSL 配置
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;

location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}

# HTTP 强制跳转 HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$host$request_uri;
}

配置 SSL 安全增强

1
2
3
4
5
6
7
8
# HSTS — 告诉浏览器永远用 HTTPS 访问
add_header Strict-Transport-Security "max-age=63072000" always;

# 防止被 iframe 嵌入
add_header X-Frame-Options "SAMEORIGIN" always;

# 禁用 MIME 嗅探
add_header X-Content-Type-Options "nosniff" always;

完成后重载 Nginx:

1
sudo nginx -t && sudo nginx -s reload

五、acme.sh 实战

acme.sh 是纯 Shell 实现的 ACME 客户端,零依赖,对国内云服务商的 DNS API 支持非常好。

安装

1
curl https://get.acme.sh | sh

安装后自动添加别名 acme.sh,配置文件和证书都在 ~/.acme.sh/

方式一:HTTP 验证(Webroot 模式)

1
2
acme.sh --issue -d your-domain.com -d www.your-domain.com \
-w /var/www/html

方式二:DNS 验证(手动)

1
2
acme.sh --issue --dns -d your-domain.com \
--yes-I-know-dns-manual-mode-enough-go-ahead-please

终端会输出需要添加的 TXT 记录,去 DNS 管理后台添加一条 TXT 记录:

  • 主机记录:_acme-challenge
  • 记录类型:TXT
  • 记录值:终端输出的那串字符

添加完成后等待 DNS 生效(通常 1-10 分钟),然后执行 renew:

1
2
acme.sh --renew -d your-domain.com \
--yes-I-know-dns-manual-mode-enough-go-ahead-please

方式三:DNS 验证(自动 — 以阿里云为例)

先设置 API 密钥:

1
2
export Ali_Key="你的AccessKey ID"
export Ali_Secret="你的AccessKey Secret"

然后自动申请:

1
acme.sh --issue --dns dns_ali -d your-domain.com -d *.your-domain.com

国内主流服务商的 DNS API 都支持:

服务商 API 前缀 示例
阿里云 dns_ali --dns dns_ali
腾讯云 dns_dp --dns dns_dp
华为云 dns_huaweicloud --dns dns_huaweicloud
Cloudflare dns_cf --dns dns_cf
DNSPod dns_dp --dns dns_dp

安装证书到 Nginx

1
2
3
4
acme.sh --install-cert -d your-domain.com \
--key-file /etc/nginx/ssl/your-domain.com.key \
--fullchain-file /etc/nginx/ssl/your-domain.com.pem \
--reloadcmd "nginx -s reload"

--reloadcmd 是证书续期后自动执行的命令,确保 Nginx 每次都加载新证书。

生成 dhparam

1
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

然后在 Nginx 中引用:

1
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

六、通配符证书

通配符证书可以保护一个域名及其所有子域名:

证书 覆盖范围
your-domain.com 仅限该域名
*.your-domain.com blog.your-domain.com, api.your-domain.com 等所有一级子域名

通配符证书只能用 DNS-01 方式验证,因为你需要证明你拥有整条域名记录的控制权。

使用 acme.sh 申请通配符证书

1
2
3
4
# DNS API 自动模式(阿里云)
export Ali_Key="xxx"
export Ali_Secret="xxx"
acme.sh --issue --dns dns_ali -d your-domain.com -d *.your-domain.com

拿到证书后,Nginx 的配置和普通证书完全一样。

七、自动续期

Let’s Encrypt 证书有效期只有 90 天,必须配置自动续期。

Certbot 续期

Certbot 安装时通常会注册 systemd timer:

1
2
3
4
5
6
7
8
# 查看定时器状态
sudo systemctl status certbot.timer

# 查看定时器定义(每天随机检查两次)
sudo systemctl list-timers | grep certbot

# 手动测试续期(不会真正续期)
sudo certbot renew --dry-run

续期逻辑:距离到期 30 天内才会真正续期,所以每天检查两次不会产生多余的请求。

acme.sh 续期

acme.sh 安装时会自动创建 crontab:

1
2
3
4
5
6
7
8
# 查看 crontab
crontab -l | grep acme

# 手动强制续期
acme.sh --renew -d your-domain.com --force

# 查看已安装的证书列表
acme.sh --list

在 Docker 中使用

如果 Nginx 运行在 Docker 容器中,续期后需要让容器重新加载:

1
2
# acme.sh 的 reloadcmd
--reloadcmd "docker exec nginx nginx -s reload"

或者绑定挂载证书目录,续期后重启容器:

1
2
3
4
5
6
7
# docker-compose.yml
services:
nginx:
image: nginx:alpine
volumes:
- /etc/letsencrypt:/etc/letsencrypt:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro

八、速率限制

Let’s Encrypt 有严格的速率限制,申请失败时首先要检查是否触发了限制:

限制项 限制值 说明
每个注册账户每周 50 个新证书 单域名最多 50 个新证书/周
每个域名每小时 5 次失败验证 验证失败 5 次后锁定 1 小时
每个证书最多域名数 100 个 一个 SAN 证书最多包含 100 个域名
重复证书 每周 5 个 相同域名集合的证书,每周最多签发 5 个

避免触发限制的建议

  • 开发测试用 --staging 环境,无速率限制
  • 不要频繁重复申请同一域名
  • 一次申请多域名用 SAN 证书,而非逐个域名申请

使用 Staging 环境测试

1
2
3
4
5
# Certbot
sudo certbot --staging ...

# acme.sh
acme.sh --issue --staging -d your-domain.com -w /var/www/html

Staging 环境签发的证书浏览器不信任,仅用于测试流程。

九、常见问题排查

1. 验证失败:Connection refused / timeout

1
2
Detail: Fetching http://your-domain.com/.well-known/acme-challenge/xxx:
Connection refused

原因排查:

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 确认 80 端口被监听
sudo ss -tlnp | grep :80

# 2. 确认防火墙放行 80 端口
sudo ufw status
sudo firewall-cmd --list-ports

# 3. 确认云服务商的安全组开放了 80 端口

# 4. 确认域名解析正确
dig your-domain.com +short
curl -I http://your-domain.com

2. 验证失败:DNS 记录未生效

1
2
3
4
5
# 检查 TXT 记录是否发布
dig _acme-challenge.your-domain.com TXT +short

# 查看 DNS 传播情况
dig _acme-challenge.your-domain.com TXT @8.8.8.8 +short

TXT 记录添加后,等 1-10 分钟生效(取决于 TTL),然后再重试。

3. 证书到期没有自动续期

1
2
3
4
5
6
7
8
9
10
11
12
# Certbot:检查定时器是否启用
sudo systemctl is-enabled certbot.timer
sudo systemctl is-active certbot.timer

# 手动触发续期
sudo certbot renew

# acme.sh:检查 crontab
crontab -l | grep acme

# 手动续期
acme.sh --renew -d your-domain.com --force

4. 证书不包含某个子域名

Let’s Encrypt 的通配符 *.your-domain.com 只覆盖一级子域名,不含 your-domain.com 自身。申请时需要同时指定:

1
acme.sh --issue --dns dns_ali -d your-domain.com -d *.your-domain.com

同样,*.blog.your-domain.com 也不会覆盖 blog.your-domain.com 本身。

5. 重载后还用旧证书

Nginx 启动时加载证书到内存,reload 不会完全重启。试一下 restart

1
sudo systemctl restart nginx

或者用浏览器隐私窗口、curl 检查确认:

1
curl -vI https://your-domain.com 2>&1 | grep -A5 "Server certificate"

6. 权限问题

Let’s Encrypt 的证书目录 /etc/letsencrypt/live/ 是符号链接,且只有 root 可读:

1
2
# 将 nginx 用户加入可读组
sudo usermod -a -G ssl-cert www-data

十、多域名和 SAN 证书

一张 SAN(Subject Alternative Name)证书可以包含多个域名,减少证书数量和管理成本:

1
2
3
4
5
# Certbot
sudo certbot --nginx -d site-a.com -d www.site-a.com -d site-b.com -d www.site-b.com

# acme.sh
acme.sh --issue --dns dns_ali -d site-a.com -d www.site-a.com -d site-b.com -d www.site-b.com

注意:所有域名放进一张证书后,任何一个域名验证失败,整张证书都拿不到。

十一、总结

核心流程三件事:

  1. 申请:选 Certbot 或 acme.sh,根据场景选 HTTP 或 DNS 验证
  2. 部署:Nginx 引用证书文件,配置 SSL 参数和 HTTP 跳转
  3. 续期:确保自动续期正常运行,定期检查

推荐的默认方案:

1
2
3
公网服务器 + 标准 Nginx → Certbot + HTTP-01,一条命令搞定
需要通配符 / 内网 / 国内 → acme.sh + DNS-01 + DNS API
零配置 → Caddy 服务器自带自动 HTTPS

Let’s Encrypt 的出现让 HTTPS 不再是奢侈品。配合自动续期,一次配置,长期受益。