DevOps

Let’s Encrypt + Docker Nginx 免费 HTTPS 配置教程

从 0 到 1 为阿里云 ECS 上的 Docker Nginx 网站配置 Let’s Encrypt 免费 HTTPS、HTTP 跳转和自动续期。

从 0 到 1 给阿里云 ECS 网站配置免费 HTTPS:Let’s Encrypt + Docker Nginx + 自动续期完整教程

一、问题背景:为什么网站需要 HTTPS?

当我们刚把网站部署到服务器上时,通常可以通过下面这种地址访问:

http://gocodestudio.cn

但是浏览器地址栏可能会显示“不安全”。

这是因为 HTTP 是明文传输协议,浏览器和服务器之间传输的数据没有加密。如果用户访问网站、提交表单、登录后台、发送接口请求,数据理论上可能被中间人监听或篡改。

HTTPS 的作用,就是在 HTTP 外面加上一层 TLS/SSL 加密。

最终访问地址会变成:

https://gocodestudio.cn

浏览器地址栏会出现小锁,表示连接是加密的。


二、HTTP 和 HTTPS 的区别

简单理解:

HTTP  = 明文通信
HTTPS = 加密通信 + 身份验证 + 防篡改

HTTP 的问题

HTTP 传输过程像一张明信片:

用户浏览器  →  明文数据  →  服务器

中间经过网络节点时,理论上别人可以看到内容。

HTTPS 的作用

HTTPS 会先建立加密连接:

用户浏览器  →  TLS 加密通道  →  服务器

它主要解决三个问题:

能力说明
加密防止传输内容被直接看到
身份验证证明你访问的确实是目标网站
防篡改防止数据在传输中被偷偷修改

三、HTTPS 的核心:SSL/TLS 证书是什么?

HTTPS 不是单靠服务器配置就能实现的,它需要一个证书。

证书的作用有点像网站的“身份证”。

浏览器访问网站时,会检查:

  1. 这个证书是不是可信机构签发的?
  2. 证书绑定的域名是不是当前访问的域名?
  3. 证书有没有过期?
  4. 证书有没有被吊销?

如果都没问题,浏览器就会认为这个网站是可信的,然后建立加密连接。


四、证书方案对比

常见 HTTPS 证书有几类:

类型价格适合场景优点缺点
阿里云免费个人测试证书免费个人测试、小型站点阿里云控制台操作方便数量和使用场景有限
Let’s Encrypt免费个人站、博客、官网、API免费、通用、支持自动续期有效期 90 天,需要自动续期
商业 DV 证书付费企业官网、普通业务稳定、服务支持较好需要付费
OV/EV 证书较贵企业、金融、品牌官网企业身份验证更强申请复杂、成本高

对于个人网站、技术博客、小型项目官网,Let’s Encrypt 已经足够好


五、为什么选择 Let’s Encrypt?

这次我们选择的是:

Let’s Encrypt 免费证书

原因很直接:

  1. 免费 不需要额外购买商业证书。

  2. 浏览器信任 主流浏览器都信任 Let’s Encrypt 证书。

  3. 适合个人网站 比如博客、官网、文档站、知识库、API 服务等。

  4. 支持自动续期 虽然证书有效期只有 90 天,但可以通过 Certbot 自动续期。

  5. 配置成熟 Nginx、Apache、Docker、Caddy 等常见部署方式都支持。


六、整体架构

这次网站部署在阿里云 ECS 上,域名是:

gocodestudio.cn
www.gocodestudio.cn

服务器公网 IP 示例是:

203.0.113.xxx

整体访问流程如下:

flowchart LR
    A["用户浏览器"] --> B["访问 https://gocodestudio.cn"]
    B --> C["DNS 解析"]
    C --> D["阿里云 ECS 公网 IP: 203.0.113.xxx"]
    D --> E["安全组放行 80/443"]
    E --> F["Docker Nginx 网关"]
    F --> G["读取 HTTPS 证书"]
    G --> H["返回网站内容"]

七、配置前需要准备什么?

在正式配置 HTTPS 之前,需要先准备好这些东西。

1. 已购买域名

例如:

gocodestudio.cn

2. 域名已备案

如果服务器在中国大陆,域名通常需要完成 ICP 备案后才能正常对外提供网站服务。

3. 域名已解析到 ECS

DNS 解析中添加 A 记录:

主机记录类型记录值
@A203.0.113.xxx
wwwA203.0.113.xxx

也就是:

gocodestudio.cn      → 203.0.113.xxx
www.gocodestudio.cn  → 203.0.113.xxx

4. 安全组放行端口

阿里云 ECS 安全组需要放行:

端口用途
80HTTP 访问、Let’s Encrypt 域名验证
443HTTPS 访问

八、Let’s Encrypt 是如何验证域名的?

Let’s Encrypt 需要确认:

你确实控制了这个域名

常见验证方式叫:

HTTP-01 Challenge

它的原理是:

  1. Certbot 向 Let’s Encrypt 申请证书
  2. Let’s Encrypt 生成一个随机校验文件
  3. Certbot 把这个文件放到服务器的指定目录
  4. Let’s Encrypt 访问:
http://gocodestudio.cn/.well-known/acme-challenge/xxxx
  1. 如果能访问到正确内容,就证明你控制这个域名
  2. 验证通过后,Let’s Encrypt 签发证书

流程图:

sequenceDiagram
    participant C as Certbot
    participant L as Let's Encrypt
    participant N as Nginx
    participant W as 网站目录

    C->>L: 申请 gocodestudio.cn 证书
    L-->>C: 返回 HTTP-01 校验任务
    C->>W: 写入校验文件
    L->>N: 访问 /.well-known/acme-challenge/xxx
    N->>W: 读取校验文件
    W-->>L: 返回正确内容
    L-->>C: 验证通过,签发证书

所以,80 端口必须能访问。 如果 80 端口没有放行,证书申请通常会失败。


九、本次服务器实际情况

这台服务器不是宿主机直接跑 Nginx,而是使用 Docker 部署。

当前结构大致是:

/home/gocodestudio/
├── docker-compose.yml
├── nginx/
│   ├── conf.d/
│   │   └── gocodestudio.conf
│   └── ssl/
│       └── gocodestudio.cn/
│           ├── fullchain.pem
│           └── privkey.pem
└── www/
    ├── own/
    │   └── index.html
    ├── godo/
    └── admin/

Nginx 容器负责监听:

80
443

证书最终放在宿主机:

/home/gocodestudio/nginx/ssl/gocodestudio.cn/

然后通过 Docker volume 挂载到容器内部:

/etc/nginx/ssl/gocodestudio.cn/

十、为什么不直接用 certbot —nginx?

很多教程会写:

sudo certbot --nginx -d example.com

这个方式适合 Nginx 直接安装在宿主机上的情况。

但这次服务器中,80443 被 Docker Nginx 容器占用,宿主机没有直接安装 Nginx。

所以更适合用:

webroot 模式

也就是:

certbot certonly --webroot

它只负责申请证书,不强行修改 Nginx 配置。

这样更安全,也更适合 Docker 场景。


十一、配置步骤详解

第一步:确认 HTTP 可以访问

先访问:

http://gocodestudio.cn

如果能看到页面,例如:

Hello! This is OWN site.

说明:

  1. 域名解析正常
  2. ECS 公网 IP 正常
  3. 安全组 80 端口正常
  4. Nginx 能正常返回页面

这是申请证书前最重要的基础条件。


第二步:配置 Nginx 的 HTTP 校验目录

为了让 Let’s Encrypt 可以访问校验文件,需要配置:

location ^~ /.well-known/acme-challenge/ {
    root /usr/share/nginx/html;
    default_type text/plain;
}

本次主域名 HTTP 配置如下:

server {
    listen 80;
    server_name gocodestudio.cn www.gocodestudio.cn;

    location ^~ /.well-known/acme-challenge/ {
        root /usr/share/nginx/html;
        default_type text/plain;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

注意:

/.well-known/acme-challenge/

不能直接跳转到 HTTPS,否则续期验证可能失败。


第三步:安装 Certbot

服务器系统是 Ubuntu,可以执行:

apt-get update
apt-get install -y certbot

安装完成后检查:

certbot --version

第四步:申请证书

使用 webroot 模式申请证书:

certbot certonly \
  --webroot \
  -w /home/gocodestudio/www \
  -d gocodestudio.cn \
  -d www.gocodestudio.cn \
  --agree-tos \
  --register-unsafely-without-email \
  --non-interactive

参数说明:

参数说明
certonly只申请证书,不自动修改 Nginx
--webroot使用 webroot 校验方式
-w /home/gocodestudio/www校验文件所在目录
-d gocodestudio.cn申请主域名证书
-d www.gocodestudio.cn同时申请 www 域名证书
--agree-tos同意 Let’s Encrypt 服务条款
--non-interactive非交互模式执行

申请成功后,证书会生成在:

/etc/letsencrypt/live/gocodestudio.cn/fullchain.pem
/etc/letsencrypt/live/gocodestudio.cn/privkey.pem

十二、Nginx 配置 HTTPS

申请成功后,把证书复制到 Docker Nginx 挂载目录:

mkdir -p /home/gocodestudio/nginx/ssl/gocodestudio.cn

install -m 644 \
  /etc/letsencrypt/live/gocodestudio.cn/fullchain.pem \
  /home/gocodestudio/nginx/ssl/gocodestudio.cn/fullchain.pem

install -m 600 \
  /etc/letsencrypt/live/gocodestudio.cn/privkey.pem \
  /home/gocodestudio/nginx/ssl/gocodestudio.cn/privkey.pem

然后添加 HTTPS server:

server {
    listen 443 ssl;
    server_name gocodestudio.cn www.gocodestudio.cn;

    ssl_certificate /etc/nginx/ssl/gocodestudio.cn/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/gocodestudio.cn/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    root /usr/share/nginx/html/own;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

检查 Nginx 配置:

docker exec gateway_nginx nginx -t

重载 Nginx:

docker exec gateway_nginx nginx -s reload

十三、HTTP 自动跳转 HTTPS

为了让用户访问:

http://gocodestudio.cn

时自动跳到:

https://gocodestudio.cn

HTTP 配置中加入:

location / {
    return 301 https://$host$request_uri;
}

完整逻辑是:

flowchart TD
    A["用户访问 http://gocodestudio.cn"] --> B["Nginx 监听 80"]
    B --> C{"是否是 ACME 校验路径?"}
    C -- 是 --> D["返回校验文件"]
    C -- 否 --> E["301 跳转到 HTTPS"]
    E --> F["用户访问 https://gocodestudio.cn"]
    F --> G["Nginx 监听 443"]
    G --> H["使用证书建立 TLS 连接"]
    H --> I["返回网站页面"]

十四、自动续期是怎么实现的?

Let’s Encrypt 证书有效期是 90 天。

这不是缺点,而是它的安全设计。证书周期短,即使密钥泄露,风险窗口也更短。

Certbot 安装后,会自动创建系统定时任务。

可以查看:

systemctl list-timers --all | grep certbot

本次服务器中,已经看到:

certbot.timer

这表示系统会定期尝试续期证书。


十五、Docker 场景下自动续期还差一步

Certbot 默认会续期:

/etc/letsencrypt/live/gocodestudio.cn/

但是 Docker Nginx 实际读取的是:

/home/gocodestudio/nginx/ssl/gocodestudio.cn/

所以需要一个续期后的 deploy hook:

/etc/letsencrypt/renewal-hooks/deploy/gocodestudio-nginx.sh

内容如下:

#!/bin/sh
set -eu

CERT_SRC="${RENEWED_LINEAGE:-/etc/letsencrypt/live/gocodestudio.cn}"
CERT_DST="/home/gocodestudio/nginx/ssl/gocodestudio.cn"

mkdir -p "$CERT_DST"

install -m 644 "$CERT_SRC/fullchain.pem" "$CERT_DST/fullchain.pem"
install -m 600 "$CERT_SRC/privkey.pem" "$CERT_DST/privkey.pem"

docker exec gateway_nginx nginx -t
docker exec gateway_nginx nginx -s reload

它的作用是:

  1. Certbot 续期成功
  2. 自动复制新证书到 Docker Nginx 挂载目录
  3. 检查 Nginx 配置
  4. 重新加载 Nginx
  5. 新证书立即生效

流程如下:

flowchart LR
    A["certbot.timer 定时执行"] --> B["certbot renew"]
    B --> C{"证书是否快过期?"}
    C -- 否 --> D["不处理"]
    C -- 是 --> E["向 Let's Encrypt 续期"]
    E --> F["新证书写入 /etc/letsencrypt"]
    F --> G["执行 deploy hook"]
    G --> H["复制证书到 /home/gocodestudio/nginx/ssl"]
    H --> I["docker exec nginx -t"]
    I --> J["docker exec nginx -s reload"]
    J --> K["新证书生效"]

十六、验证是否配置成功

1. 验证 HTTP 是否跳转 HTTPS

curl -I http://gocodestudio.cn/

返回中应该看到:

HTTP/1.1 301 Moved Permanently
Location: https://gocodestudio.cn/

2. 验证 HTTPS 是否正常

curl -I https://gocodestudio.cn/

返回:

HTTP/1.1 200 OK

3. 验证页面内容

curl https://gocodestudio.cn/

返回:

<h1>Hello! This is OWN site.</h1>

4. 查看证书信息

certbot certificates

本次结果:

Certificate Name: gocodestudio.cn
Domains: gocodestudio.cn www.gocodestudio.cn
Expiry Date: 2026-09-20
Certificate Path: /etc/letsencrypt/live/gocodestudio.cn/fullchain.pem
Private Key Path: /etc/letsencrypt/live/gocodestudio.cn/privkey.pem

5. 模拟自动续期

certbot renew --dry-run

如果看到:

Congratulations, all simulated renewals succeeded

说明自动续期流程正常。


十七、如何借助 AI 远程完成配置?

这次配置使用了一个比较安全的 AI 远程协助方式:临时 SSH 公钥

不是把服务器密码发给 AI,也不是长期开放权限。

整体流程

sequenceDiagram
    participant U as 用户
    participant AI as AI 助手
    participant S as ECS 服务器

    AI->>AI: 生成临时 SSH 密钥对
    AI-->>U: 提供临时公钥
    U->>S: 把公钥加入 authorized_keys
    AI->>S: 使用临时私钥 SSH 登录
    AI->>S: 检查环境并配置 HTTPS
    AI->>S: 验证 HTTPS 和自动续期
    AI->>S: 删除临时公钥
    AI-->>U: 返回配置结果

用户需要做什么?

用户只需要把 AI 提供的公钥加入服务器:

mkdir -p ~/.ssh
chmod 700 ~/.ssh

echo 'ssh-ed25519 xxxxx codex-temp-gocodestudio-https' >> ~/.ssh/authorized_keys

chmod 600 ~/.ssh/authorized_keys

配置完成后,再删除这条临时公钥。

这次操作中,AI 已经在完成配置后删除了临时授权。

这种方式的好处

方式安全性
直接发服务器密码不推荐
开远程桌面权限大,操作不透明
临时 SSH 公钥推荐,可随时删除
临时子账号 + 最小权限更适合企业环境

临时公钥方式的优点是:

  1. 不暴露服务器密码
  2. 可以随时删除授权
  3. 操作可以通过命令记录追踪
  4. 适合短时间远程协助

十八、本次最终效果

配置完成后:

http://gocodestudio.cn/

会自动跳转到:

https://gocodestudio.cn/

并且下面两个地址都可以正常访问:

https://gocodestudio.cn/
https://www.gocodestudio.cn/

证书覆盖域名:

gocodestudio.cn
www.gocodestudio.cn

自动续期也已经验证成功。


十九、常见问题

1. 为什么申请证书失败?

常见原因:

  1. 域名没有解析到服务器 IP
  2. ECS 安全组没有开放 80
  3. Nginx 没有正确返回 ACME 校验文件
  4. DNS 还没生效
  5. 域名没有备案或访问被拦截

可以先检查:

curl http://gocodestudio.cn/

再检查校验目录:

curl http://gocodestudio.cn/.well-known/acme-challenge/test

2. 为什么 HTTPS 能访问,但 HTTP 不跳转?

检查 Nginx 的 80 配置是否有:

return 301 https://$host$request_uri;

然后执行:

docker exec gateway_nginx nginx -t
docker exec gateway_nginx nginx -s reload

3. 为什么续期成功但浏览器还是旧证书?

Docker 场景下,很可能是:

Certbot 续期了 /etc/letsencrypt
但 Nginx 读取的是另一个目录

解决方式就是使用 deploy hook,把新证书复制到 Nginx 挂载目录,然后 reload Nginx。


4. 是否可以只申请 gocodestudio.cn,不申请 www?

可以,但不推荐。

如果用户访问:

www.gocodestudio.cn

而证书没有包含 www,浏览器会报证书不匹配。

所以通常一起申请:

-d gocodestudio.cn -d www.gocodestudio.cn

二十、安全建议

HTTPS 配好之后,还要注意服务器安全。

尤其是这些端口:

端口建议
22只允许自己的 IP 访问
5432不建议直接暴露公网
3389Linux 服务器一般不需要开放
80可以开放,用于跳转和证书续期
443必须开放,用于 HTTPS

数据库端口 5432 如果直接暴露公网,会有被扫描和爆破的风险。更推荐:

  1. 只允许内网访问
  2. 只允许指定 IP 访问
  3. 使用 Docker 内部网络访问数据库
  4. 使用强密码和最小权限账号

二十一、总结

这次我们完成了一个完整的 HTTPS 配置流程:

域名解析
→ ECS 安全组开放 80/443
→ 确认 HTTP 可访问
→ 使用 Certbot webroot 申请 Let’s Encrypt 证书
→ Docker Nginx 配置 HTTPS
→ HTTP 自动跳转 HTTPS
→ 配置自动续期 hook
→ 验证续期 dry-run 成功
→ 删除 AI 临时 SSH 公钥

最终效果:

https://gocodestudio.cn

可以安全访问网站,并且证书会自动续期。

对于个人网站、技术博客、项目官网来说,Let’s Encrypt 是非常合适的免费 HTTPS 方案。它免费、可靠、自动化程度高,只要一次配置正确,后续基本不需要手动维护。