4.Nginx HTTPS 部署与证书管理

2026年6月10日 Nginx_Docker 30 分钟阅读 67 次阅读
📖 文章摘要

讲解 Ubuntu 下 Certbot 一键申请免费 SSL 证书,Nginx 配置 HTTPS 强制跳转,包含证书自动续期、端口放行全套实操命令。

Nginx HTTPS 部署与证书管理

一、HTTPS 基础概念

为什么需要 HTTPS

HTTP 是明文传输,中间人可以窃听、篡改数据。HTTPS = HTTP + TLS 加密,保证:

  • 机密性:数据加密,第三方看不到内容
  • 完整性:数据不会被篡改
  • 身份验证:浏览器能确认网站是真的 demo.test.com,不是钓鱼网站

HTTPS 和 HTTP 的区别

对比 HTTP HTTPS
端口 80 443
加密 TLS 加密
证书 不需要 需要 SSL 证书
浏览器显示 不安全警告 地址栏锁图标

二、TLS 握手流程

1. 浏览器访问 https://demo.test.com
2. Nginx 收到请求,发现是 443 端口
3. Nginx 把证书(fullchain.pem,含公钥)发给浏览器
4. 浏览器验证证书:
   - 这个证书是签发给 demo.test.com 的吗?→ 是
   - 证书过期了吗?→ 没有
   - 签发机构(Let's Encrypt)可信吗?→ 可信
5. 浏览器自己生成一个随机的对称密钥
6. 浏览器用 Nginx 的公钥加密这个对称密钥,发给 Nginx
7. Nginx 用私钥(privkey.pem)解密,拿到对称密钥
8. 之后双方用这个对称密钥加密所有通信数据

为什么用非对称加密交换密钥,之后用对称加密?

非对称加密很慢,对称加密很快。所以只在握手阶段用非对称加密交换一个密钥,之后全部用对称加密——兼顾安全和性能。


三、证书文件详解

fullchain.pem(证书/公钥)

  • 包含:你网站的公钥 + 中间 CA 证书 + 根证书(完整证书链)
  • 用途:发给浏览器,让浏览器验证网站身份
  • 位置:/etc/letsencrypt/live/demo.test.com/fullchain.pem

privkey.pem(私钥)

  • 包含:你网站的私钥
  • 用途:服务器用来解密浏览器发来的对称密钥
  • 位置:/etc/letsencrypt/live/demo.test.com/privkey.pem
  • 绝对不能泄露! 泄露了等于 HTTPS 形同虚设

cert.pem(网站证书,不含证书链)

  • 只包含你网站的证书,不含中间 CA 证书
  • 一般用 fullchain.pem 而不是 cert.pem,避免浏览器验证时找不到中间证书

四、Nginx HTTPS 配置(博客的实际配置)

server {
    server_name demo.test.com www.demo.test.com;
    listen 443 ssl;                              # 监听 443 端口,启用 SSL
    ssl_certificate /etc/letsencrypt/live/demo.test.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/demo.test.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    ...
}

server {
    listen 80;                                   # HTTP 端口
    server_name demo.test.com www.demo.test.com ...;
    return 301 https://$server_name$request_uri; # 强制跳转 HTTPS
}

逐行解释

指令 作用
listen 443 ssl 监听 443 端口,启用 TLS 加密
ssl_certificate 证书文件路径(公钥+证书链),发给浏览器验证
ssl_certificate_key 私钥文件路径,服务器用来解密
include options-ssl-nginx.conf 引入 certbot 自动生成的安全 SSL 参数
ssl_dhparam Diffie-Hellman 参数文件,增强密钥交换安全性
listen 80 + return 301 HTTP 强制跳转 HTTPS

为什么 listen 80 要 301 跳转?

用户在浏览器输入 http://demo.test.com(注意是 http),请求会走到 listen 80 的 server 块。如果不做跳转,用户就一直停在不安全的 HTTP 上。

return 301 https://$server_name$request_uri; 告诉浏览器:"你该用 https",浏览器收到 301 后自动跳转到 https://demo.test.com。


五、certbot 的 SSL 安全参数

/etc/letsencrypt/options-ssl-nginx.conf 文件内容:

ssl_session_cache shared:le_nginx_SSL:10m;      # SSL 会话缓存,10MB 共享内存
ssl_session_timeout 1440m;                       # 会话超时 24 小时
ssl_session_tickets off;                         # 关闭 session ticket(更安全)

ssl_protocols TLSv1.2 TLSv1.3;                  # 只允许 TLS 1.2 和 1.3
ssl_prefer_server_ciphers off;                   # 不强制服务端选择加密套件

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:..."; # 允许的加密算法

核心要点

  • 只允许 TLS 1.2 和 1.3:老的 SSLv3、TLS 1.0/1.1 已经不安全,不支持
  • 加密算法用 GCM 模式:目前最安全的对称加密方式
  • ssl_session_cache:缓存 SSL 会话,避免每次连接都重新握手(性能优化)
  • ssl_session_tickets off:关闭 session ticket 可防止某些攻击,安全性更高

六、Let's Encrypt + certbot 工作机制

Let's Encrypt 是什么

免费的 SSL 证书颁发机构(CA)。以前 SSL 证书要花钱买,Let's Encrypt 让任何人都能免费获得可信证书。

certbot 是什么

自动化的证书管理工具,帮你完成:申请证书 → 配置 Nginx → 自动续期。

申请证书的完整流程

1. 你执行:certbot --nginx -d demo.test.com -d www.demo.test.com

2. certbot 向 Let's Encrypt 证明你拥有这个域名:
   - Let's Encrypt 在你的网站根目录放一个验证文件
   - Let's Encrypt 访问 http://demo.test.com/.well-known/acme-challenge/xxx
   - 如果能访问到 → 证明你控制这个域名
   - 验证通过

3. Let's Encrypt 签发证书

4. certbot 自动把证书路径写入 Nginx 配置

5. certbot 自动设置定时续期任务

自动续期机制(systemd timer)

本博客的服务器用的是 systemd timer 方式:

systemctl cat certbot.timer
OnCalendar=*-*-* 00,12:00:00     # 每天 0:00 和 12:00 各检查一次
RandomizedDelaySec=43200          # 随机延迟 0~12 小时(避免所有服务器同时请求)
Persistent=true                   # 错过执行时间,开机后补执行
ExecStart=/usr/bin/certbot -q renew --no-random-sleep-on-renew
# -q 静默模式
# renew 检查所有证书,如果剩余有效期 < 30 天就自动续期

查看证书信息

# 查看证书到期时间
sudo certbot certificates

# 手动测试续期(不实际执行)
sudo certbot renew --dry-run

七、HTTPS 完整请求链路

用户输入 https://demo.test.com

浏览器 → DNS 解析 → shturl.cc/OFB6
       → TCP 三次握手(端口 443)
       → TLS 握手:
           Nginx 发送 fullchain.pem
           浏览器验证证书
           浏览器生成对称密钥,用公钥加密发给 Nginx
           Nginx 用 privkey.pem 解密
       → HTTP 请求(对称加密)
           Nginx 匹配 location → 转发给后端
       → 返回响应(对称加密)
       → 浏览器渲染页面

八、从零开始:SSL 证书配置实操

实操环境

  • 云服务商:UCloud
  • 服务器位置:香港(免备案)
  • 公网 IP:192.0.2.1
  • 域名:example.com
  • 服务:OpenClaw(Docker 容器,端口 18789)
  • 系统:Ubuntu + Nginx

前置条件:确认 certbot 已安装

# 查看 certbot 是否已安装
ubuntu@server:~$ which certbot && certbot --version
/usr/bin/certbot
certbot 2.9.0

# 如果没安装,执行以下命令:
sudo apt update
sudo apt install -y certbot python3-certbot-nginx

安装说明:

  • certbot - 核心工具,负责申请和续期证书
  • python3-certbot-nginx - Nginx 插件,能自动修改 Nginx 配置

第一步:删除现有证书(模拟裸机状态)

模拟一台没有 SSL 证书的服务器,从零开始:

ubuntu@server:~$ sudo certbot delete --cert-name example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certificate(s) are selected for deletion:

  * example.com

WARNING: Before continuing, ensure that the listed certificates are not being
used by any installed server software (e.g. Apache, nginx, mail servers).
Deleting a certificate that is still being used will cause the server software
to stop working. See https://certbot.org/deleting-certs for information on
deleting certificates safely.

Are you sure you want to delete the above certificate(s)?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
Deleted all files relating to certificate example.com.

输出解释:

  • WARNING - 警告:如果证书正在被 Nginx 使用,删除后网站会无法访问
  • (Y)es/(N)o - 确认删除,输入 y 回车

第二步:准备 Nginx 配置(HTTP 临时配置)

删除证书后,Nginx 配置不能引用已删除的证书文件,需要一个纯 HTTP 的临时配置:

ubuntu@server:~$ cat /etc/nginx/conf.d/example.conf
# 反向代理(HTTP 临时配置)
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:18789;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

重载 Nginx 使配置生效:

ubuntu@server:~$ sudo nginx -t && sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: the configuration file /etc/nginx/nginx.conf test is successful

此时状态: 网站只能通过 http://example.com 访问,显示"不安全"


第三步:申请 SSL 证书

ubuntu@server:~$ sudo certbot --nginx -d example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for example.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2026-10-07.
These files will be updated when the certificate renews.

Deploying certificate
Successfully deployed certificate for example.com to /etc/nginx/conf.d/example.conf
Congratulations! You have successfully enabled HTTPS on https://example.com

certbot 自动做了什么:

  1. 域名验证:在 http://example.com/.well-known/acme-challenge/ 放置验证文件
  2. 申请证书:Let's Encrypt 验证通过后签发证书
  3. 下载证书:保存到 /etc/letsencrypt/live/example.com/
  4. 修改 Nginx:自动添加 SSL 配置到 Nginx
  5. 重载 Nginx:使配置生效

第四步:查看证书状态

ubuntu@server:~$ sudo certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: example.com
    Serial Number: 56bf92f7cc2bc49296c6819f7c681e8e86c
    Key Type: ECDSA
    Domains: example.com
    Expiry Date: 2026-10-07 06:54:18+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

关键信息:

  • VALID: 89 days - 证书还有 89 天到期
  • Key Type: ECDSA - 使用 ECDSA 加密(比 RSA 更快更安全)

第五步:配置自动续期

5.1 什么是 systemd timer?

systemd timer 是 Linux 的定时任务机制(类似 Windows 的计划任务)。certbot.timer 会定期检查证书是否需要续期。

5.2 查看 timer 配置

ubuntu@server:~$ systemctl cat certbot.timer
# /usr/lib/systemd/system/certbot.timer
[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target

配置说明:

  • OnCalendar=*-*-* 00,12:00:00 - 每天 0:00 和 12:00 各检查一次
  • RandomizedDelaySec=43200 - 随机延迟 0~12 小时(避免所有服务器同时请求 Let's Encrypt)
  • Persistent=true - 如果错过了执行时间(比如关机了),开机后补执行

5.3 检查 timer 是否运行

ubuntu@server:~$ systemctl list-timers | grep certbot
Thu 2026-07-09 18:05:14 HKT 2h 11min Thu 2026-07-09 01:29:18 HKT - certbot.timer certbot.service
ubuntu@server:~$ systemctl is-active certbot.timer
active

5.4 如果 timer 没启用,手动启用

# 启用开机自启
sudo systemctl enable certbot.timer

# 立即启动
sudo systemctl start certbot.timer

# 验证
systemctl is-active certbot.timer
# 输出 active 表示已运行

第六步:测试自动续期

ubuntu@server:~$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

看到 "Congratulations" 说明自动续期配置成功。

--dry-run 只是模拟测试,不会真的续期证书。


第七步:查看 certbot 修改后的 Nginx 配置

ubuntu@server:~$ cat /etc/nginx/conf.d/example.conf
server {
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:18789;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name example.com;
    return 404; # managed by Certbot
}

certbot 自动添加了:

  • listen 443 ssl - 启用 HTTPS
  • ssl_certificate - 证书路径
  • ssl_certificate_key - 私钥路径
  • include options-ssl-nginx.conf - 安全参数
  • ssl_dhparam - DH 参数
  • return 301 - HTTP 自动跳转 HTTPS

注意: 标有 # managed by Certbot 的行由 certbot 自动管理,不要手动修改。


第八步:重载 Nginx 并测试访问

# 重载 Nginx
ubuntu@server:~$ sudo nginx -t && sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: the configuration file /etc/nginx/nginx.conf test is successful

# 测试 HTTPS 访问
ubuntu@server:~$ curl -I https://example.com
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)

看到 HTTP/1.1 200 OK 说明 HTTPS 配置成功。


九、常见问题排查

问题1:certbot 申请证书失败

错误: Challenge failed for domain example.com

原因: 域名没有正确解析到服务器,或 80 端口无法访问

排查步骤:

# 1. 检查域名解析是否指向服务器 IP
nslookup example.com

# 2. 检查 80 端口是否开放
curl -I http://example.com/.well-known/acme-challenge/test

# 3. 检查防火墙
sudo ufw status

问题2:Nginx 启动失败

错误: cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file

原因: Nginx 配置引用了不存在的证书文件

解决:

# 1. 检查配置语法
sudo nginx -t

# 2. 如果证书不存在,先修改配置去掉 SSL 部分
# 3. 然后重新申请证书
sudo certbot --nginx -d example.com

问题3:自动续期失败

查看续期日志:

sudo journalctl -u certbot.service -n 50

常见原因:

  • DNS 解析失败(域名指向了错误的 IP)
  • 80 端口被防火墙阻挡
  • Let's Encrypt 服务器暂时不可用

十、运维最佳实践

1. 备份策略

# 备份证书和 Nginx 配置
sudo tar -czf ssl-backup-$(date +%Y%m%d).tar.gz /etc/letsencrypt/ /etc/nginx/conf.d/

# 备份到远程服务器
scp ssl-backup-*.tar.gz user@remote-server:/backup/

2. 监控证书到期

# 查看证书到期时间
sudo certbot certificates | grep "Expiry Date"

# 写一个脚本检查并告警(可加入 cron 定期执行)
#!/bin/bash
EXPIRY=$(sudo certbot certificates | grep "Expiry Date" | awk '{print $3}')
DAYS=$(( ($(date -d "$EXPIRY" +%s) - $(date +%s)) / 86400 ))
if [ $DAYS -lt 30 ]; then
    echo "证书将在 $DAYS 天后过期!"
fi

3. 服务迁移清单

迁移服务器时需要迁移:

  • /etc/letsencrypt/ - 证书文件
  • /etc/nginx/conf.d/ - Nginx 配置
  • 域名 DNS 解析(更新 A 记录指向新 IP)

十一、问答

Q:HTTPS 是怎么工作的?
浏览器发请求 → Nginx 发送证书(公钥) → 浏览器验证证书 → 浏览器生成对称密钥用公钥加密发给服务器 → 服务器用私钥解密 → 之后用对称密钥加密通信。

Q:为什么用非对称加密交换密钥,之后用对称加密?
非对称加密慢,对称加密快。握手阶段用非对称加密交换密钥,之后用对称加密传输数据,兼顾安全和性能。

Q:SSL 证书怎么来的?
Let's Encrypt 免费签发,certbot 工具自动申请。certbot 会验证你拥有这个域名,通过后自动签发证书并配置 Nginx。

Q:证书怎么续期?
certbot 设置了 systemd timer,每天检查两次,如果证书剩余有效期不足 30 天就自动续期。无需手动操作。

Q:listen 80 和 listen 443 的关系?
80 是 HTTP 端口,443 是 HTTPS 端口。listen 80 的 server 块做 301 跳转到 HTTPS,listen 443 ssl 的 server 块处理正式请求。

Q:fullchain.pem 和 privkey.pem 分别是什么?
fullchain.pem 是证书(含公钥和证书链),发给浏览器验证身份。privkey.pem 是私钥,服务器用来解密浏览器发来的对称密钥。私钥绝对不能泄露。

Q:certbot 删除证书后 Nginx 会挂吗?
会的。如果 Nginx 配置引用了已删除的证书,nginx -t 会报错,Nginx 无法启动。需要先修改配置去掉 SSL 部分,或者重新申请证书。

Q:systemd timer 和 cron 有什么区别?

  • cron:传统的定时任务,配置简单但功能有限
  • systemd timer:更现代,支持错过执行补执行(Persistent=true)、随机延迟等高级功能

Q:为什么需要随机延迟(RandomizedDelaySec)?
如果所有服务器都在同一时刻请求 Let's Encrypt,会造成类似 DDoS 的效果。随机延迟可以让请求分散在不同时间。

Q:证书快到期了但自动续期失败怎么办?

  1. 查看日志:sudo journalctl -u certbot.service
  2. 手动续期:sudo certbot renew
  3. 如果手动也失败,检查域名解析和 80 端口是否正常
  4. 紧急情况:重新申请证书 sudo certbot --nginx -d example.com

说明:文中域名、IP均为教学演示虚拟地址,无真实线上服务

文章创建于:2026年6月10日CC BY-NC-SA 4.0

评论

暂无评论,来写第一条吧

别老叽霸扫描爆破后台了,个人博客能存啥有价值的东西,有这时间不如去扫俩放片的网站