首页 / 经验库 / 正文

Nginx + Certbot 免费 HTTPS 证书与自动续期(Webroot 方式)

阅读:176 更新:2026-06-29 免费

本文记录一次 Nginx 使用 Certbot 申请 HTTPS 证书时,旧证书因 manual 方式无法自动续期的问题。适合使用 Nginx、希望免费部署 SSL 证书的站长阅读。看完可完成 Webroot 验证、首次签发、HTTPS 配置、定时自动续期及续期后自动重载 Nginx。

实践参考 预计落地20–40 分钟 减少试错1–3 小时 适用前提域名已解析到服务器,80 端口可访问
本文以 site.example.com 为示例域名。请将它替换成自己的真实域名。
本文假设 Nginx 安装目录为 /usr/local/nginx,并使用 /etc/init.d/nginx reload 平滑重载。若你的服务器使用 systemctl 管理 Nginx,将对应命令替换为 systemctl reload nginx

1. 适用范围

适用于:

  • 单个域名或子域名,例如 site.example.com
  • DNS 已解析到当前服务器公网 IP
  • 使用 Nginx 对外提供 HTTP/HTTPS 服务
  • 希望通过 Let's Encrypt + Certbot 免费申请并自动续期证书
  • 使用 HTTP-01 验证,也就是 Let’s Encrypt 通过 80 端口访问验证文件

2. 前提检查

2.1 DNS 与端口

申请前必须满足:

  1. site.example.com 的 A 记录已经解析到当前服务器公网 IP。
  2. 服务器安全组、防火墙已放行 TCP 80443 端口。
  3. Nginx 已启动,并能从公网访问 http://site.example.com

可检查解析结果:

ping site.example.com
Ping 被禁并不一定代表 DNS 有问题;重点是域名解析出的 IP 是否为当前服务器公网 IP。

2.2 安装 Certbot(Ubuntu/Debian)

已安装可跳过:

apt update
apt install -y certbot

确认版本:

certbot --version

3. 创建统一的 ACME 验证目录

mkdir -p /usr/local/nginx/acme-webroot/.well-known/acme-challenge

这个目录用于临时放置 Let's Encrypt 的验证文件。

创建 Nginx 公共配置片段:

cat > /usr/local/nginx/conf/certbot-acme.conf <<'EOF'
location ^~ /.well-known/acme-challenge/ {
    root /usr/local/nginx/acme-webroot;
    default_type "text/plain";
    try_files $uri =404;
}
EOF

4. 第一次申请证书:先配置 HTTP 站点

在 Nginx 已启用的虚拟主机配置目录中,新建或修改域名配置。例如:

vi /usr/local/nginx/conf/vhost/site.example.com.conf

首次申请证书时,先使用下面的 HTTP 配置:

server {
    listen 80;
    server_name site.example.com;

    include /usr/local/nginx/conf/certbot-acme.conf;

    location / {
        return 404;
    }
}

说明:

  • /.well-known/acme-challenge/ 由 Certbot 验证路径专门处理。
  • 其他路径暂时返回 404,这是首次部署、尚未配置 HTTPS 时的安全默认行为。
  • 不要在尚未申请到证书前就强制跳转 HTTPS。

检查并平滑加载 Nginx:

/usr/local/nginx/sbin/nginx -t && /etc/init.d/nginx reload

5. 先验证 ACME 路径是否可被公网访问

创建一个测试文件:

echo "certbot-ok" > /usr/local/nginx/acme-webroot/.well-known/acme-challenge/test.txt

访问测试:

curl http://site.example.com/.well-known/acme-challenge/test.txt

正常应返回:

certbot-ok

验证完成后可以删除测试文件:

rm -f /usr/local/nginx/acme-webroot/.well-known/acme-challenge/test.txt

6. 申请正式证书

certbot certonly --webroot \
  --cert-name site.example.com \
  -w /usr/local/nginx/acme-webroot \
  -d site.example.com

成功后,证书文件通常在:

/etc/letsencrypt/live/site.example.com/fullchain.pem
/etc/letsencrypt/live/site.example.com/privkey.pem

查看已申请的所有证书:

certbot certificates
若一个证书需要包含多个域名,例如 site.example.comwww.site.example.com,首次申请时必须同时写出所有域名,并确保每个域名都已解析到本服务器:
certbot certonly --webroot \
   --cert-name site.example.com \
   -w /usr/local/nginx/acme-webroot \
   -d site.example.com \
   -d www.site.example.com

7. 申请成功后,改为 HTTP 跳转 HTTPS

将虚拟主机配置改成下面形式:

server {
    listen 80;
    server_name site.example.com;

    include /usr/local/nginx/conf/certbot-acme.conf;

    location / {
        return 301 https://site.example.com$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name site.example.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_certificate     /etc/letsencrypt/live/site.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/site.example.com/privkey.pem;

    # 按实际项目配置网站根目录、PHP、反向代理等。
    location / {
        return 404;
    }
}

检查并平滑加载 Nginx:

/usr/local/nginx/sbin/nginx -t && /etc/init.d/nginx reload

验证:

curl -I http://site.example.com
curl -I https://site.example.com

预期:

  • HTTP 返回 301,并跳转到 HTTPS。
  • HTTPS 能正常建立连接。
  • /.well-known/acme-challenge/ 仍可在 HTTP 下访问,供未来自动续期使用。

8. 启用 Certbot 自动续期定时器

Ubuntu/Debian 常用的 Certbot 定时器:

systemctl enable --now certbot.timer

查看状态:

systemctl status certbot.timer

看到类似以下状态,说明已启用:

Loaded: loaded (...; enabled; ...)
Active: active (waiting)

Certbot 会定期检查全部证书;证书接近到期时才会真正续期,不会每天重复签发。

9. 续期成功后自动重载 Nginx

证书文件更新后,Nginx 需要 reload 才能立即载入新证书。

创建 Certbot 的 deploy hook:

mkdir -p /etc/letsencrypt/renewal-hooks/deploy

cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'EOF'
#!/bin/sh
/usr/local/nginx/sbin/nginx -t && /etc/init.d/nginx reload
EOF

chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

确认脚本内容:

cat /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Certbot 会在任意证书真实续期成功后,自动执行此目录中具有执行权限的脚本。

手动检查脚本是否正常:

/usr/local/nginx/sbin/nginx -t && /etc/init.d/nginx reload
若系统使用 systemd 管理 Nginx,可将脚本中的第二部分改为:
systemctl reload nginx

10. 模拟续期测试

只测试当前证书,避免服务器上其他旧证书影响结果:

certbot renew --cert-name site.example.com --dry-run

成功时会看到类似:

The following certs were successfully renewed:
  /etc/letsencrypt/live/site.example.com/fullchain.pem (success)

--dry-run 是模拟测试:

  • 会验证域名、HTTP 验证路径和续期配置;
  • 不会替换正式证书;
  • 不会改变现有证书到期时间。
不建议在存在旧证书的服务器上直接以 certbot renew --dry-run 测试全部证书。
旧证书若曾用 manual 方式申请,可能无法自动续期,从而显示失败;这不会代表当前 Webroot 证书配置失败。

11. 常见问题

11.1 curl 验证文件返回 404

检查:

  1. Nginx 配置是否已 reload
  2. certbot-acme.conf 是否确实被 include 在当前域名的 server {} 内。
  3. 验证文件是否位于:
   /usr/local/nginx/acme-webroot/.well-known/acme-challenge/
  1. 域名是否解析到当前服务器。

11.2 Certbot 提示连接超时或无法验证

通常是:

  • 域名 A/AAAA 记录不正确;
  • 80 端口未开放;
  • 云服务器安全组未开放 80 端口;
  • 另一个 Web 服务器或 CDN 接管了该域名;
  • HTTP 被强制跳转到错误站点或错误域名。

11.3 旧证书提示 manual plugin 无法自动续期

说明该证书以前是用手动 DNS/文本验证方式申请的,缺少自动验证脚本。

解决方式:后续为该域名补上 Webroot 验证路径后,用本文的 certbot certonly --webroot ... 方式重新申请或更新该证书。

11.4 为什么优先使用 reload,不使用 restart

推荐:

/usr/local/nginx/sbin/nginx -t && /etc/init.d/nginx reload

原因:

  • 先检查配置语法;
  • 配置错误时不会重载;
  • 平滑加载新配置和新证书;
  • 通常不会中断现有访问。

restart 会先停止再启动;如果新配置有错误,可能导致 Nginx 无法重新启动,网站暂时不可访问。

12. 常用命令速查

# 查看已申请证书
certbot certificates

# 查看自动续期定时器
systemctl status certbot.timer

# 只模拟测试某一张证书
certbot renew --cert-name site.example.com --dry-run

# 检查 Nginx 配置并平滑重载
/usr/local/nginx/sbin/nginx -t && /etc/init.d/nginx reload

# 查看 Certbot 续期成功后的重载脚本
cat /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh