结论先行
对外公开域名统一走 443 SSL,并做 80 → 301 跳转;内部管理域名保持 80 不变。证书用 certbot certonly 只签发、不改配置,配置完全由仓库文件控制。上线顺序固定:先签发证书,再覆盖新配置。
对外域名上 443
9 个对外域名新增 443 ssl 块,80 端口统一 301 跳转到 HTTPS。
内部域名保持 80
nacos、monitor、ossrh 等内部管理域名不暴露公网 HTTPS,维持 80 不变。
certbot 只签不改
用 certonly 只签发证书,不触碰 nginx 配置,避免证书与配置相互干扰。
443 块引用了证书路径,证书不存在时 nginx -t 会直接失败、nginx 无法启动。因此必须先完成证书签发,再把新配置覆盖到服务器,最后 nginx -t 通过后再 reload。
问题背景
现网 nginx.conf 中,除 www.sparrowzoo.com 已有证书外,其余域名都只配置了 80 端口。所有对外公开域名——静态站点、反向代理、甚至带 WebSocket 的 API——都走明文 HTTP,请求内容可被中间人窃听或篡改。
HTTPS 通过 TLS 加密传输,保证机密性与完整性,是浏览器安全基线(安全上下文、Service Worker、剪贴板等能力)的前置条件。本次改动的目标是:为所有对外公开域名启用 HTTPS,内部管理域名保持 80 不变,证书通过 Certbot(Let's Encrypt)自动签发与续期。
- 443 SSL · 加密端口
- HTTPS 默认端口,
listen 443 ssl表示该 server 块启用 TLS。 - 301 跳转 · 永久重定向
- 浏览器与搜索引擎都会记住跳转,80 端口访问被永久转到 HTTPS。
- Certbot · 证书客户端
- Let's Encrypt 官方工具,自动完成域名校验、签发与续期。
- X-Forwarded-Proto
- 反向代理向后端透传原始协议,后端据此生成 https 链接与 secure cookie。
Certbot 的 HTTP-01 校验会临时在 80 端口放一个文件让 Let's Encrypt 访问。因此所有待签发域名的 A 记录必须指向本服务器,且 80 端口对外可达。这也是「先签证书、再覆盖配置」的原因之一——签发时旧配置里的 80 块仍在正常工作。
详细内容
STEP 01域名清单:对外与内部的边界
先把域名按「是否对外公开」分成两类。只有对外公开的域名才需要证书与 443 块;内部管理域名继续走 80,避免把管理面板暴露到公网 HTTPS 的证书与端口管理里。
对外公开(启用 HTTPS,80 → 301 跳转 443)
| 域名 | 类型 | 后端 / root | 备注 |
|---|---|---|---|
r.sparrowzoo.net | 静态 | /path/to/source | expires 1h |
passport.sparrowzoo.com | 静态 | /path/to/passport | try_files |
admin.sparrowzoo.com | 静态 | /path/to/admin | try_files |
im.sparrowzoo.com | 静态 | /path/to/im | try_files |
www.sparrowzoo.com | 静态 | /path/to/www | 已有证书 |
img.im.sparrowzoo.net | 静态 | /path/to/images | expires 0d |
u.r.sparrowzoo.net | 静态 | /path/to/upload | expires 0d |
lottery.sparrowzoo.com | 反代 | http://lottery (127.0.0.1:<PORT>) | no-store |
api.sparrowzoo.com | 反代 | http://api (127.0.0.1:<PORT>) + /websocket → http://websocket (127.0.0.1:<PORT>) | 含 WebSocket |
内部管理(保持 80,不上 HTTPS)
| 域名 | 类型 | 后端 / root |
|---|---|---|
nacos.example.com | 反代 | http://nacos (127.0.0.1:<PORT>) |
monitor.example.com | 反代 | http://monitor (127.0.0.1:<PORT>) |
ossrh.example.com | 静态 | /path/to/ossrh |
分类原则:能公开访问的域名才需要加密与证书;内部运维面板走内网或由 IP 白名单保护,不必占用 Let's Encrypt 的证书名额。
STEP 02本次配置改动
改动集中在五点:对外域名加 443 块、反代透传协议、修复语法 bug、清理无效写法、移除废弃的 juejin 配置。
- 新增 443 ssl 块 + 80→301:每个对外域名新增
listen 443 ssl块,原 80 块改为return 301 https://$host$request_uri;。 - 透传协议头:反向代理新增
proxy_set_header X-Forwarded-Proto $scheme;,后端据此判断协议,用于生成 https 链接与 secure cookie。 - 修复语法 bug:原配置
server_name api.sparrowzoo.com缺少分号,会让nginx -t直接失败。 - 清理无效写法:
index /;→index index.html;、charset utf_8;→charset utf-8;、移除对 proxy_pass 无意义的fastcgi_intercept_errors on;。 - 移除 juejin 配置:删除 juejin 相关的 upstream 与 server 块。
静态站点:80→301 + 443 ssl
# 80:统一重定向到 HTTPS
server {
listen 80;
server_name passport.sparrowzoo.com;
return 301 https://$host$request_uri;
}
# 443:静态站点
server {
listen 443 ssl;
server_name passport.sparrowzoo.com;
ssl_certificate /etc/letsencrypt/live/passport.sparrowzoo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/passport.sparrowzoo.com/privkey.pem;
root /path/to/passport;
index index.html;
charset utf-8;
location / {
try_files $uri $uri/ /index.html;
}
}反向代理 + WebSocket:透传协议头
server {
listen 80;
server_name api.sparrowzoo.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name api.sparrowzoo.com;
ssl_certificate /etc/letsencrypt/live/api.sparrowzoo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.sparrowzoo.com/privkey.pem;
location / {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# WebSocket 升级
location /websocket {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
}
}证书路径由 Certbot 统一放在 /etc/letsencrypt/live/<域名>/ 下,fullchain.pem 是证书链、privkey.pem 是私钥。覆盖配置前要确认这些路径与实际签发目录一致。
STEP 03上线步骤:先签证书,再覆盖配置
整个过程七步,核心原则是证书在前、配置在后,每一步都有明确的验证点。
第 1 步 · 备份现网配置
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak-$(date +%F)备份带日期后缀,出问题可以一键还原。
第 2 步 · 确认 DNS 与 80 端口
所有待签发域名的 A 记录需指向本服务器,且 80 端口对外可达(Certbot 的 HTTP-01 校验依赖)。这一步不通过,后面签发会失败。
第 3 步 · 先用 certbot 签发证书
此时服务器仍是旧配置,80 块正常。需新签发的 8 个域名:r.sparrowzoo.net、img.im.sparrowzoo.net、u.r.sparrowzoo.net、passport/admin/im/lottery/api.sparrowzoo.com。
for d in r.sparrowzoo.net img.im.sparrowzoo.net u.r.sparrowzoo.net \
passport.sparrowzoo.com admin.sparrowzoo.com im.sparrowzoo.com \
lottery.sparrowzoo.com api.sparrowzoo.com; do
sudo certbot certonly --nginx -d "$d"
donecertonly 只签发证书、不修改 nginx 配置,配置完全由仓库文件控制;--nginx(不带 certonly)则会直接改写配置,容易和仓库文件冲突。
第 4 步 · 覆盖新配置到服务器
将仓库中的 nginx.conf 覆盖到服务器 /etc/nginx/nginx.conf,并核对 pid 路径、各 root 路径与服务器实际目录一致。
第 5 步 · 校验并平滑重载
sudo nginx -t # 必须先通过
sudo nginx -s reloadnginx -t 是必须通过的前置校验;失败说明配置有语法错误或证书路径缺失,此时不要 reload。
第 6 步 · 验证
curl -I https://api.sparrowzoo.com # 应 200
curl -I http://passport.sparrowzoo.com # 应 301 跳到 https并人工确认 im 聊天(wss)、图片加载、文件上传等正常——HTTPS 下这些走 wss:// 与加密上传,要实测链路。
第 7 步 · 确认自动续期
sudo certbot renew --dry-runLet's Encrypt 证书有效期 90 天,Certbot 会自动续期。--dry-run 做一次模拟续期,确认定时任务与校验链路都正常。
STEP 04注意事项
- 顺序不能反:
443块引用了证书路径,证书不存在时nginx -t会失败、nginx 无法启动。必须先签发证书,再覆盖新配置。 - 证书与配置解耦:用
certonly只签证书,配置完全由仓库文件控制,避免 Certbot 自动改写引入差异。 - 内部域名不上 HTTPS:nacos、monitor 等管理面板保持 80,不暴露到公网 HTTPS。
- WebSocket 要单独验证:HTTPS 下客户端用
wss://,升级头要正确透传,im 聊天才能正常工作。
DEPLOYMENT DECISION
对外上 443,内部保 80,证书先于配置。
对外公开域名统一 443 SSL + 80→301 跳转,内部管理域名保持 80;证书用 certbot certonly 只签不改,配置由仓库文件控制。
上线七步可归为一句:备份、确认 DNS、签发证书、覆盖配置、nginx -t 通过后 reload、验证、模拟续期。真正的风险点只有一个——证书没签好就覆盖了引用证书路径的配置,nginx 起不来,所以顺序永远不能反。
常见问题
按文档上线后 HTTPS 依然不生效?大多数情况不是 nginx 配置写错,而是服务器之外的网络入口没放行 443 端口——最常见的就是云服务器安全组只开了 22(SSH),没开 80 / 443。下面给出「本机自测三步法」,先把问题隔离到正确的层次。
Q1HTTPS 没生效,浏览器报「无法访问此网站 / ERR_CONNECTION_CLOSED」
现象
访问 https://www.sparrowzoo.com 时,浏览器提示「无法访问此网站…ERR_CONNECTION_CLOSED」或长时间无响应。
排查:本机自测三步法
依次执行下面三条命令。每条「通过」都代表 nginx 的某一层是好的,通过后往下走,直到找出断在哪一层。
第 1 步 · 校验配置与证书
sudo nginx -t输出 syntax is ok / test is successful 说明配置语法正确、证书路径都能加载。若失败,多半是证书没签好或路径写错。
第 2 步 · 确认监听端口
sudo ss -tlnp | grep -E ':(80|443)\b'同时出现 0.0.0.0:80 和 0.0.0.0:443 说明 reload 已生效、nginx 正在监听两个端口。只有 80 没有 443,说明还在跑旧配置,先执行 sudo nginx -s reload。
第 3 步 · 本机直连 443
curl -kv https://127.0.0.1 -H "Host: www.sparrowzoo.com"本机能完成 TLS 握手并返回内容,说明 nginx 的 HTTPS 本身完全正常。此时问题一定出在服务器之外:云安全组、防火墙或 DNS。
如果第 1、2、3 步都通过,但外网浏览器仍连不上 HTTPS,基本可以锁定是云服务器安全组没有放行 443(或 80)端口。阿里云 ECS 默认安全组往往只开了 22,80 / 443 需要手动添加。
解决:在阿里云安全组放行 80 / 443
- 进入安全组:登录 阿里云 ECS 控制台 → 网络与安全 → 安全组 ↗,找到实例绑定的安全组(如
sg-2zea8r0aht2wpup5yc90)。 - 添加入方向规则:点「手动添加」,分别加两条——端口
80(TCP)、授权对象0.0.0.0/0;端口443(TCP)、授权对象0.0.0.0/0。 - 保存后重试:安全组规则即时生效、无需重启,直接刷新
https://www.sparrowzoo.com验证。
安全组是云服务器最外层的「网络防火墙」,独立于系统内的 iptables / ufw。它默认丢弃未放行端口的入站流量,因此即使 nginx 在系统内正常监听 443,外网流量也会在到达实例前被安全组拦下。
官方依据与阅读
下列文档支撑 HTTPS 与 Certbot 的机制说明;域名分类与上线顺序是结合本项目背景给出的部署方案。文档核对日期:2026-09-27。
- [01]Nginx · Configuring HTTPS Servers ↗
listen 443 ssl、ssl_certificate 与 ssl_certificate_key 的标准写法。
- [02]Nginx · ngx_http_core_module: return ↗
return 301 重定向的语义与
$host、$request_uri变量。 - [03]Nginx · ngx_http_proxy_module: proxy_set_header ↗
X-Forwarded-Proto 等代理头如何向后端透传原始协议。
- [04]Certbot · Documentation ↗
certonly 只签不改的用法、HTTP-01 校验与 renew 自动续期。
- [05]Let's Encrypt ↗
免费证书机构与 90 天证书有效期说明。