从 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 不是单靠服务器配置就能实现的,它需要一个证书。
证书的作用有点像网站的“身份证”。
浏览器访问网站时,会检查:
- 这个证书是不是可信机构签发的?
- 证书绑定的域名是不是当前访问的域名?
- 证书有没有过期?
- 证书有没有被吊销?
如果都没问题,浏览器就会认为这个网站是可信的,然后建立加密连接。
四、证书方案对比
常见 HTTPS 证书有几类:
| 类型 | 价格 | 适合场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 阿里云免费个人测试证书 | 免费 | 个人测试、小型站点 | 阿里云控制台操作方便 | 数量和使用场景有限 |
| Let’s Encrypt | 免费 | 个人站、博客、官网、API | 免费、通用、支持自动续期 | 有效期 90 天,需要自动续期 |
| 商业 DV 证书 | 付费 | 企业官网、普通业务 | 稳定、服务支持较好 | 需要付费 |
| OV/EV 证书 | 较贵 | 企业、金融、品牌官网 | 企业身份验证更强 | 申请复杂、成本高 |
对于个人网站、技术博客、小型项目官网,Let’s Encrypt 已经足够好。
五、为什么选择 Let’s Encrypt?
这次我们选择的是:
Let’s Encrypt 免费证书原因很直接:
免费 不需要额外购买商业证书。
浏览器信任 主流浏览器都信任 Let’s Encrypt 证书。
适合个人网站 比如博客、官网、文档站、知识库、API 服务等。
支持自动续期 虽然证书有效期只有 90 天,但可以通过 Certbot 自动续期。
配置成熟 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.cn2. 域名已备案
如果服务器在中国大陆,域名通常需要完成 ICP 备案后才能正常对外提供网站服务。
3. 域名已解析到 ECS
DNS 解析中添加 A 记录:
| 主机记录 | 类型 | 记录值 |
|---|---|---|
@ | A | 203.0.113.xxx |
www | A | 203.0.113.xxx |
也就是:
gocodestudio.cn → 203.0.113.xxx
www.gocodestudio.cn → 203.0.113.xxx4. 安全组放行端口
阿里云 ECS 安全组需要放行:
| 端口 | 用途 |
|---|---|
80 | HTTP 访问、Let’s Encrypt 域名验证 |
443 | HTTPS 访问 |
八、Let’s Encrypt 是如何验证域名的?
Let’s Encrypt 需要确认:
你确实控制了这个域名常见验证方式叫:
HTTP-01 Challenge它的原理是:
- Certbot 向 Let’s Encrypt 申请证书
- Let’s Encrypt 生成一个随机校验文件
- Certbot 把这个文件放到服务器的指定目录
- Let’s Encrypt 访问:
http://gocodestudio.cn/.well-known/acme-challenge/xxxx- 如果能访问到正确内容,就证明你控制这个域名
- 验证通过后,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 直接安装在宿主机上的情况。
但这次服务器中,80 和 443 被 Docker Nginx 容器占用,宿主机没有直接安装 Nginx。
所以更适合用:
webroot 模式也就是:
certbot certonly --webroot它只负责申请证书,不强行修改 Nginx 配置。
这样更安全,也更适合 Docker 场景。
十一、配置步骤详解
第一步:确认 HTTP 可以访问
先访问:
http://gocodestudio.cn如果能看到页面,例如:
Hello! This is OWN site.说明:
- 域名解析正常
- ECS 公网 IP 正常
- 安全组 80 端口正常
- 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.cnHTTP 配置中加入:
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它的作用是:
- Certbot 续期成功
- 自动复制新证书到 Docker Nginx 挂载目录
- 检查 Nginx 配置
- 重新加载 Nginx
- 新证书立即生效
流程如下:
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 OK3. 验证页面内容
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.pem5. 模拟自动续期
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 公钥 | 推荐,可随时删除 |
| 临时子账号 + 最小权限 | 更适合企业环境 |
临时公钥方式的优点是:
- 不暴露服务器密码
- 可以随时删除授权
- 操作可以通过命令记录追踪
- 适合短时间远程协助
十八、本次最终效果
配置完成后:
http://gocodestudio.cn/会自动跳转到:
https://gocodestudio.cn/并且下面两个地址都可以正常访问:
https://gocodestudio.cn/
https://www.gocodestudio.cn/证书覆盖域名:
gocodestudio.cn
www.gocodestudio.cn自动续期也已经验证成功。
十九、常见问题
1. 为什么申请证书失败?
常见原因:
- 域名没有解析到服务器 IP
- ECS 安全组没有开放 80
- Nginx 没有正确返回 ACME 校验文件
- DNS 还没生效
- 域名没有备案或访问被拦截
可以先检查:
curl http://gocodestudio.cn/再检查校验目录:
curl http://gocodestudio.cn/.well-known/acme-challenge/test2. 为什么 HTTPS 能访问,但 HTTP 不跳转?
检查 Nginx 的 80 配置是否有:
return 301 https://$host$request_uri;然后执行:
docker exec gateway_nginx nginx -t
docker exec gateway_nginx nginx -s reload3. 为什么续期成功但浏览器还是旧证书?
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 | 不建议直接暴露公网 |
3389 | Linux 服务器一般不需要开放 |
80 | 可以开放,用于跳转和证书续期 |
443 | 必须开放,用于 HTTPS |
数据库端口 5432 如果直接暴露公网,会有被扫描和爆破的风险。更推荐:
- 只允许内网访问
- 只允许指定 IP 访问
- 使用 Docker 内部网络访问数据库
- 使用强密码和最小权限账号
二十一、总结
这次我们完成了一个完整的 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 方案。它免费、可靠、自动化程度高,只要一次配置正确,后续基本不需要手动维护。
