HTTPS 启动指南 (Let's Encrypt)
HTTPS (Hypertext Transfer Protocol Secure, 超文本传输安全协议),可以有效抵御中间人攻击,杜绝信息在网络传输过程中被监视和篡改的可能。其实 HTTPS 就是 HTTP 协议套了一层 TLS 协议,用来加密 HTTP 数据报。关于 TLS 协议的具体内容,这里就不细致展开了。这篇文章主要聊聊免费 HTTPS 的相关概念以及如何给自己的网站和服务器申请并应用。
基本概念
SSL/TLS
TLS (Transport Layer Security) 协议的前身是 SSL (Secure Sockets Layer) 协议,由于 SSL 不安全,后面才有了 TLS 协议。最新且主流的 TLS 版本是 TLS 1.3,部分较老的客户端可能还在使用 TLS 1.2。TLS 1.1 及以下的版本不安全,网站没必要支持它们。
公钥/私钥
TLS 加密的原理是非对称加密,简单来说就是使用公钥加密的内容,只有私钥才能解开。公钥会包含在证书里,任何人都可以查看。私钥必须存储在服务器上,不能泄露。
证书
历史原因,SSL 证书,TLS 证书,SSL/TLS 证书等等说法其实都是同一概念,为了避免混淆,以下统一简称证书。证书是由 CA 颁发的,相当于给网站的身份证明。TLS 连接的时候会用到证书,浏览器会根据证书检查网站的身份。在大多数浏览器中,网页地址栏旁边有个锁标志,点击其中可以查看网站的证书及颁发它的 CA。申请证书需要域名(还有 IP 证书,不了解,下面也不介绍)。证书具有有效期,在有效期内,证书才能被浏览器信任。过了有效期需要续签证书。有了证书才能使用 TLS 加密 HTTP 内容,进行 HTTPS 通信。
CA (Certificate Authority, 证书颁发机构)
颁发证书的权威机构,有根 CA 和中间 CA。浏览器访问某个网站时,会向 CA 检查网站的证书是否有效。操作系统的安装镜像和浏览器的安装包里会存放一些根 CA 的证书。根 CA 被无条件信任。这样,浏览器访问网站的时候,信任链就是:根 CA -> 中间 CA -> 网站。其中有一个环节出错,都会导致浏览器发出不安全警告。
DV/OV/EV
Domain Validation (DV), Organization Validation (OV), Extended Validation (EV)。证书的信任等级。申请 DV 证书只需要验证申请者拥有该域名。而申请 OV 证书和 EV 证书需要验证申请者的组织,这可以提高客户的信任程度(得加钱!)。免费证书只能是 DV 了。
ACME (Automatic Certificate Management Environment, 自动证书管理环境)
ACME 协议是 DV 证书得以免费的关键。因为证书能自动颁发、续签、吊销,没有人工成本。
Let’s Encrypt
Let’s Encrypt 是今天的头号主角,维基百科是这么说的:
Let’s Encrypt是由网际网路安全研究小组(ISRG)运营的非营利性证书颁发机构,免费提供传输层安全性协定(TLS)加密的X.509证书。它是世界上最大的证书颁发机构,超过3亿个网站使用其服务,其目标是确保所有网站的安全并使用HTTPS。
Let’s Encrypt 使用 ACME 协议颁发证书。
Certbot
主要向 Let’s Encrypt 自动申请和续签 DV 证书的工具,使用 ACME 协议,用 Python 编写。
申请原理
详细申请原理可以看这里。Let’s Encrypt 只能颁发 DV 证书,所以只要能验证你拥有某个域名,就可以给你颁发证书了。验证方式主要有两种:
- HTTP-01:向网站某目录下放置一个指定名字和内容的文件,然后 Let’s Encrypt 检查该网址能够访问,并且内容也正确,就认为你拥有该域名。
- DNS-01:给域名添加一条指定名字和内容的 TXT 记录,然后 Let’s Encrypt 检查该 DNS 记录存在且内容正确,就认为你拥有该域名。
自动化
证书的续签必须能做到自动化,即无需人工操作。这么做的原因有很多,主要有三点:
- 省去了人工操作的麻烦,如果每隔一段时间就要手动连接服务器并输入续签命令,会带来心理负担。
- 一旦有一段时间忘记了手动续签证书,网站就无法使用 HTTPS 连接,风险大大增加。
- Let’s Encrypt 的证书有效期比较短,只有90天。而且到了 2028 年 Let’s Encrypt 会把这一时间缩短到 45 天,手动续签更麻烦了。
不少云服务商或 CDN 商集成了 Let’s Encrypt 的证书申请和自动续签流程,使用这些服务可能会更方便。
注意两点:
- 证书的私钥存放在哪取决于谁申请了证书。如果是云服务商或 CDN 商申请了证书,私钥就由它们保管。如果是你自己申请的证书,私钥就由你自己保管。
- 同一个子域名可以被多张证书包含。浏览器访问网站时,服务器可以决定这次连接使用哪张证书。
下面开始介绍在服务器上自行申请证书。
安装 Certbot
在网站服务器上,Debian 等 Linux 发行版可以使用以下命令安装 Certbot:
sudo apt install certbot
使用系统包管理器安装 Certbot 比较方便,因为会自动配置 Systemd 的定时任务。在 Debian 12 上,Certbot 定时任务每天运行两次,检查当前时间是否满足续签条件,满足了就会续签一张新的证书。
域名验证方式:HTTP-01
通常来说,向网站某目录放置一个文件是很容易的。比如,在 Nginx 上,可以添加如下配置(参考这里):
# 直接在 server 块里加上这个 location 块
# 这样配置的好处是已有的配置不需要调整
# 注意 WAF 有没有拦截这种 . 开头的路径
location /.well-known/acme-challenge/ {
# /path/to/acme-root 可以是任意文件夹,但不要轻易变化
alias /path/to/acme-root/.well-known/acme-challenge/;
}
然后,运行以下命令为www.example.com申请一张证书。可以添加多个-d,实现一张证书包含多个具体子域名。--deploy-hook指定一个可执行程序,只在成功获取证书后运行,可以用来重新加载 WEB 服务器(如systemctl reload nginx),以便读取新证书,或者运行证书格式转换等。
sudo certbot certonly \
--webroot -w /path/to/acme-root \
--deploy-hook /path/to/deploy-hook \
-d www.example.com
第一次运行的话,Certbot 会要求输入一个邮箱(似乎可以随便填写)。这个邮箱之前会用来收到证书临期提醒,2025 年开始不会了。
如果一切正常,Certbot 会和 Let’s Encrypt 通信,在/path/to/acme-root下创建.well-known/acme-challenge/文件夹和指定名字和内容的文件,Let’s Encrypt 会访问http://<域名>/.well-known/acme-challenge/<文件>验证其内容,之后文件夹会被删除,证书就申请完毕了。
申请完成后,Certbot 会提示证书存放位置,通常在/etc/letsencrypt/live/下。
域名验证方式:DNS-01
修改域名的 DNS 记录没有太通用的方法,因为不同的 DNS 服务商有不同的 API。某些 DNS 服务商有对应的 Certbot 插件,可以方便地使用,参考 Certbot 的文档。使用 DNS-01 验证的参数是--preferred-challenges=dns。
比如,给www.example.com通过 DNS-01 方式申请证书,需要添加一条 TXT 类型的 DNS 记录,记录名是_acme-challenge.www.example.com,值是 Let’s Encrypt 生成的一串值。Let’s Encrypt 会验证该记录是否存在,以及值是否正确。之后可以删除该记录。
虽然 DNS-01 不太方便,但是有些情况下必须使用。比如对于 DNS 服务商来说,它们给客户提供 Let’s Encrypt 的自动申请与续签,只能使用 DNS-01 来验证。再如,通配符域名证书如*.example.com必须使用 DNS-01 的方式获取,因为通配符域名不对应一个具体的主机名,不存在相应网址。
Certbot 提供了--manual参数,可以手动添加续签证书所需要的 DNS 记录,验证完成后清理记录。但是,为了追求我们的自动化目标,我们还要使用另外两个参数,--manual-auth-hook和--manual-cleanup-hook。它们指定两个可执行文件,分别为创建记录的代码和清理记录的代码。Certbot 会在申请证书时和申请完成时分别执行两个可执行文件并传入相应的环境变量,实现 DNS-01 验证的自动化。
我之后会发一篇文章,记录我编写 Python 脚本自动修改阿里云 ESA 的 DNS,完成 DNS-01 验证的过程。
查看证书
sudo certbot certificates
以上命令会列出本机申请的所有证书,每张证书类似于:
Certificate Name: www.example.com
Serial Number: 5*********************************4
Key Type: ECDSA
Domains: www.example.com
Expiry Date: 2026-08-10 10:50:56+00:00 (VALID: 88 days)
Certificate Path: /etc/letsencrypt/live/www.example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/www.example.com/privkey.pem
Certbot 有些子命令可以使用--cert-name参数,值就是上面的 Certificate Name。
续签证书
# 尝试续签本机所有证书
sudo certbot renew
# 续签指定证书
sudo certbot renew --cert-name <证书名>
# 模拟一遍证书续签流程,此时颁发的证书无效,并且也不会存到磁盘中
# 可以用来验证新的续签方法和参数是否有效,有效后再删掉该参数正式申请
sudo certbot renew --cert-name <证书名> --dry-run
# 强制续签指定证书
# Let's Encrypt 的速率限制挺宽松的,只要不是频繁续签证书,它不会限制
sudo certbot renew --cert-name <证书名> --force-renewal
# 未指定验证方法和相关参数时,证书续签时的验证方式和申请时一样。
# 如果指定了新的验证方式或者参数,并且强制续签证书,
# Certbot 就会使用新方法和参数续签新证书,以后续签也用新方法:
sudo certbot renew \
--cert-name www.example.com \
--preferred-challenges=dns --manual \
--manual-auth-hook /path/to/authenticator \
--manual-cleanup-hook /path/to/cleanup \
--deploy-hook /path/to/deploy-hook \
--force-renewal
之所以有个强制选项,是因为即使使用了renew子命令,Certbot 也还是会检查当前时间和证书有效期,当前时间超过阈值后,才会真正执行更新逻辑,具体阈值看文档,Certbot 不同版本的阈值不一样,对于 Debian 12 的 Certbot 来说,这个阈值是固定的,证书失效前 30 天。加上强制选项可以马上续签证书。
吊销证书
如果一张证书已经不再使用,可以吊销它。Certbot 会询问是否要删除证书,如果不删除,下次renew的时候 Certbot 仍然会尝试续签该证书。吊销前确保证书已经不在任何地方使用。
sudo certbot revoke --cert-name www.example.com
开启 HTTPS
当使用 Nginx 时,在 server 块中添加或修改如下配置项,然后重新加载 Nginx,即可为网站开启 HTTPS:
# 监听 HTTPS 的 443 端口,在该端口上启用 SSL(其实是 TLS)
listen 443 ssl;
# 下面指定的证书需要包含这个子域名,否则客户端会认为连接不安全
server_name www.example.com;
# 包含网站证书、中间 CA 的证书
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
# 证书对应的私钥
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
# 中间 CA 的证书
# Certbot 的文档提到如果 Nginx 使用了 OCSP stapling,该配置项必须为 chain.pem,
# 用来验证 OCSP 的响应
ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/chain.pem;