SSL 证书与 HTTPS:用 Certbot 0成本启动全站加密

📂 所属阶段:第六阶段 — 上线部署(生产篇)
🔗 相关章节:Gunicorn 与 Nginx · Docker 部署全流程


1. 为什么上线前必须上 HTTPS?

咱们先来看最直观的场景对比——用户输入密码的那一刻:

HTTP(裸奔模式,踩过坑的都懂 😭)
┌─────────────┐  明文传输  ┌──────────────┐  明文截获  ┌─────────────┐
│  用户浏览器  │ ─────────> │  中间节点/WiFi │ ─────────> │  黑客控制台  │
└─────────────┘            └──────────────┘            └─────────────┘
HTTPS(加密保险箱模式 ✅)
┌─────────────┐  SSL/TLS 加密  ┌──────────────┐  加密绕过  ┌─────────────┐
  用户浏览器 ─────────────>  中间节点/WiFi ─────────>  黑客(啥也看不到)
└─────────────┘                 └──────────────┘            └─────────────┘
  密文直接转发
                        ┌──────────────┐
  你的后端服务器 只有你有私钥解密
                        └──────────────┘

除了最核心的防数据泄露,HTTPS 现在还有5个不可替代的生产级优势

  1. 防 ISP/公共WiFi 的流量劫持+恶意广告插入
  2. 防中间人篡改你的 HTML/CSS/JS(比如给按钮加钓鱼链接)
  3. SEO 硬门槛:Google/Baidu 现在直接把 HTTPS 设为搜索排序加分项,甚至有的纯 HTTP 站会被降权
  4. 浏览器信任标识:Chrome/Firefox 现在把纯 HTTP 站标红加「不安全」提示,转化率直接跌20%+
  5. 开启 HTTP/2 协议的前提:HTTP/2 能把网站加载速度提30%-50%,但所有主流浏览器都要求 HTTPS 才支持

2. 0成本搞定90天自动续期的证书

国内很多免费证书要么是1年换一次手动麻烦,要么是只支持单域名,要么是来路不明的小机构。全球最靠谱的免费证书方案,绝对是 Let's Encrypt + Certbot 的组合拳

2.1 先搞懂俩角色

  • Let's Encrypt(非盈利证书颁发机构):背后有 Google、Microsoft、Mozilla 等大厂背书,所有主流浏览器都信任,证书永久免费、支持多域名/通配符、90天有效期
  • Certbot(官方自动化工具):帮你一键「申请证书→配置Web服务器→设置自动续期」,全程不超过5分钟

2.2 安装 Certbot(主流系统一键走)

Certbot 对不同系统的适配都很完善,直接用包管理器就行:

# Ubuntu / Debian(最常用的云服务器系统)
sudo apt update && sudo apt install -y certbot python3-certbot-nginx

# CentOS 7 / RHEL 7
sudo yum install -y epel-release && sudo yum install -y certbot python3-certbot-nginx

# CentOS 8+ / RHEL 8+ / Rocky Linux
sudo dnf install -y certbot python3-certbot-nginx

# macOS(本地测试用)
brew install certbot

2.3 新手推荐:Nginx 全自动配置版

如果你已经用 Nginx 配置好了 HTTP 站点(比如前面学过的 Gunicorn + Nginx),直接用这个命令就行,Certbot 会自动帮你完成所有步骤:

# 把 daomanpy.com 换成你自己的主域名,www 可以加多个二级域名
sudo certbot --nginx -d daomanpy.com -d www.daomanpy.com

输入命令后会有4个简单的交互:

  1. 邮箱地址:用来接收证书过期的紧急通知(只发2-3封,不会垃圾邮件)
  2. 同意服务条款:必须选 A(Agree)
  3. 是否共享邮箱给 EFF:选 Y 或 N 都行(EFF 是推动网络隐私的非营利组织)
  4. 是否自动跳转 HTTPS强烈建议选 2(Redirect,强制把所有 HTTP 请求301永久重定向到 HTTPS,防止 Mixed Content 问题)

完成后你会看到「Congratulations!」的提示,刷新浏览器,你的网站就已经有小绿锁(或者安全盾牌)了!

2.4 进阶/自定义版:只拿证书自己配

如果你用的不是 Nginx,或者不想让 Certbot 改你的 Nginx 配置,也可以只申请证书,自己手动配置Web服务器

# --nginx 这里是用「Nginx临时验证」的方式验证域名所有权
# --webroot 是另一种常用的验证方式(Nginx配置了静态目录的话)
# 以下两种选一种就行:

# 方式1:Nginx临时验证(推荐,不用改现有配置)
sudo certbot certonly --nginx -d daomanpy.com -d www.daomanpy.com

# 方式2:Webroot验证(假设你的Nginx静态目录是/var/www/html)
sudo certbot certonly --webroot -w /var/www/html -d daomanpy.com -d www.daomanpy.com

申请成功后,证书会放在 /etc/letsencrypt/live/你的域名/ 目录下(软链接指向最新的证书,续期后不用改路径),我们只需要两个文件:

  • fullchain.pem:证书链(给浏览器看的,包含中间证书和根证书)
  • privkey.pem:私钥(只有你自己的服务器有,千万别泄露!)

3. 进阶版安全 Nginx HTTPS 配置

如果用了 Certbot 的全自动配置,它会给你生成一个基础的 HTTPS 配置,但要拿到 SSLLabs 的 A+ 评分(生产级安全标准),还要加一些额外的安全配置。

这里给大家一个经过验证的、兼容主流浏览器(Chrome 60+、Firefox 55+、Safari 12+)的完整 HTTPS 配置模板,直接替换你的 /etc/nginx/conf.d/你的域名.conf 就行:

# /etc/nginx/conf.d/daomanpy.conf
# 注意:把所有 daomanpy.com 换成你自己的域名

# --------------------------
# 1. HTTP → HTTPS 强制重定向(301永久跳转)
# --------------------------
server {
    listen 80;
    listen [::]:80; # IPv6支持(云服务器一般都支持,加上更好)
    server_name daomanpy.com www.daomanpy.com;

    # 保留 Let's Encrypt 的验证路径(续期时必须能访问)
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    # 其他所有请求301跳转
    return 301 https://$host$request_uri;
}

# --------------------------
# 2. HTTPS 主站配置
# --------------------------
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name daomanpy.com www.daomanpy.com;

    # 根目录和入口(如果是静态站需要,反向代理可以不加,但加了更规范)
    root /var/www/daoman;
    index index.html index.htm;

    # --------------------------
    # SSL 核心配置(必须改路径)
    # --------------------------
    ssl_certificate /etc/letsencrypt/live/daomanpy.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/daomanpy.com/privkey.pem;

    # --------------------------
    # SSL 安全优化(Mozilla 推荐的 Intermediate 级配置)
    # --------------------------
    ssl_protocols TLSv1.2 TLSv1.3; # 只留安全的协议版本(TLS1.0/1.1 已被禁用)
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; # 只留安全的加密套件
    ssl_prefer_server_ciphers off; # 让浏览器自己选最优的加密套件(TLS1.3 不需要,但兼容老版本)
    ssl_session_cache shared:SSL:10m; # SSL 会话缓存(减少握手时间,提升性能)
    ssl_session_timeout 1d; # 会话缓存有效期1天
    ssl_session_tickets off; # 关闭会话票证(防止部分安全漏洞)

    # --------------------------
    # OCSP Stapling(优化证书验证速度)
    # --------------------------
    ssl_stapling on; # 开启 OCSP Stapling(服务器代替浏览器去验证证书有效性,减少网络延迟)
    ssl_stapling_verify on; # 验证 OCSP 响应的有效性
    resolver 8.8.8.8 8.8.4.4 valid=300s; # DNS 解析器(Google的,也可以换国内的114.114.114.114)
    resolver_timeout 5s; # DNS 超时时间5秒

    # --------------------------
    # 安全响应头(防止常见的 Web 攻击)
    # --------------------------
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # HSTS(强制浏览器1年内只访问HTTPS,preload可选但需要申请)
    add_header X-Frame-Options "SAMEORIGIN" always; # 防止点击劫持
    add_header X-Content-Type-Options "nosniff" always; # 防止 MIME 类型嗅探
    add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 控制 Referer 头的发送
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; # 控制浏览器API的访问权限

    # --------------------------
    # 反向代理到 Gunicorn(Flask/Django 等后端项目需要)
    # --------------------------
    location / {
        proxy_pass http://127.0.0.1:8000; # Gunicorn 监听的端口
        proxy_http_version 1.1;
        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; # 告诉后端当前是 HTTPS 协议
        proxy_redirect off;
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        client_max_body_size 20M; # 允许上传的最大文件大小(根据项目调整)
    }

    # --------------------------
    # 静态文件(Nginx 直接服务,比后端快10倍+)
    # --------------------------
    location /static/ {
        alias /var/www/daoman/static/; # 静态文件目录(根据项目调整)
        expires 30d; # 静态文件缓存30天
        add_header Cache-Control "public, immutable"; # 告诉浏览器缓存期间不用重新请求
        access_log off; # 静态文件访问日志可以关,减少磁盘IO
    }

    # --------------------------
    # 健康检查(负载均衡/监控需要)
    # --------------------------
    location /health {
        return 200 "healthy\n";
        add_header Content-Type text/plain;
        access_log off;
    }
}

配置完成后,记得先验证 Nginx 配置是否正确,再重启 Nginx:

# 验证配置
sudo nginx -t

# 如果显示「syntax is ok」和「test is successful」,再重启
sudo systemctl reload nginx # 用 reload 不用 restart,避免服务中断

4. 90天自动续期:一劳永逸的设置

Let's Encrypt 的证书只有90天有效期,这是为了安全考虑(万一私钥泄露,影响时间也短)。Certbot 自带自动续期功能,我们只需要设置一个定时任务就行。

4.1 先测试续期是否正常

在正式设置定时任务之前,一定要先用 dry-run 模式测试(不会真的续期,只是验证流程):

sudo certbot renew --dry-run

如果显示「Congratulations, all renewals succeeded」,说明测试通过!

4.2 配置定时续期(两种主流方式)

方式1:systemd timer(Ubuntu 18.04+ / CentOS 8+ 推荐)

现在的主流 Linux 发行版默认已经安装了 Certbot 的 systemd timer,只需要检查是否启动即可

# 检查 timer 状态
sudo systemctl status certbot.timer

# 如果没启动,手动启动并设置开机自启
sudo systemctl enable --now certbot.timer

# 查看所有 systemd timers,确认 certbot.timer 在里面
sudo systemctl list-timers

默认的 timer 是每天凌晨2点左右检查续期(有随机偏移,避免同时请求 Let's Encrypt 服务器),如果续期成功,还会自动 reload Nginx(Certbot 会自动检测你用的是 Nginx 还是 Apache)。

方式2:crontab(老系统兼容)

如果你的系统没有 systemd,或者想自己控制续期时间,可以用 crontab:

# 编辑 root 用户的 crontab(必须用 root,因为证书目录只有 root 能访问)
sudo crontab -e

# 在文件末尾添加这一行(每天凌晨3点检查续期,续期成功后 reload Nginx)
0 3 * * * certbot renew --quiet --renew-hook "systemctl reload nginx"

5. 检查你的 HTTPS 安全等级

配置完成后,一定要用在线工具检测一下安全等级,确保没有漏洞:

5.1 在线检测(推荐)

5.2 本地简单测试

# 测试 SSL 连接是否正常
openssl s_client -connect daomanpy.com:443 -servername daomanpy.com

# 查看证书的详细信息(有效期、颁发机构等)
openssl x509 -in /etc/letsencrypt/live/daomanpy.com/fullchain.pem -text -noout

6. 避坑指南:常见的 HTTPS 问题

6.1 Mixed Content(混用 HTTP/HTTPS)警告

如果浏览器显示「小绿锁旁边有个感叹号」,或者控制台有 Mixed Content 的错误,说明你的网站里还有HTTP 协议的资源(比如图片、CSS、JS、字体)。

解决方法:把所有资源的 URL 改成 https:// 开头,或者直接用 // 开头的相对协议(自动跟随当前页面的协议)。

6.2 证书续期失败

如果续期时显示「Failed authorization procedure」,一般是因为Let's Encrypt 的验证路径无法访问

  • 检查 Nginx 配置里的 .well-known/acme-challenge/ 路径是否正确
  • 检查防火墙是否开放了 80 端口(续期时必须用 80 端口验证)
  • 检查域名解析是否正常(ping 你的域名 看看能不能通)

7. 小结

全站 HTTPS 现在已经是生产级项目的标配,用 Let's Encrypt + Certbot 0成本就能搞定,全程不超过10分钟。

快速回顾部署流程:

  1. 安装 Certbot 和对应的 Web 服务器插件
  2. certbot --nginx -d 你的域名 全自动申请+配置
  3. (可选)替换成进阶版的安全 Nginx 配置
  4. 检查自动续期是否正常
  5. 用 SSLLabs 检测安全等级

💡 生产级最后提醒

  1. 一定要设置 HSTS 头,防止用户误访问 HTTP 站点
  2. 定期(比如每月)用 SSLLabs 检测一下安全等级
  3. 千万不要把 privkey.pem 私钥上传到 Git 仓库!

🔗 扩展阅读