🎯 目标:将运行在 152 机器上的前端 (Vite) + 后端 (NestJS) 以及 168 机器上的 MinIO 集群 + Nginx 代理,全部升级为 HTTPS,并确保分享、上传、下载功能完整可用。
📌 网络拓扑与服务器角色
公网入口 (路由器端口映射)
│
域名 www.fox360.cn
│
┌──────┴──────┐
│ │
152机器 168机器
┌─────────┐ ┌──────────────────┐
│ 前端5173│ │ MinIO 节点1-8 │
│ 后端3000│ │ (9000内网) │
└─────────┘ │ Nginx 代理 │
│ 9001(控制台HTTPS) │
│ 9002(API HTTPS) │
└──────────────────┘
- 152 机器 (192.168.39.152):前端 Vite (5173),后端 NestJS (3000)
- 168 机器 (192.168.39.168):MinIO 集群 (8节点,内网 9000),Nginx 反向代理 (9001/9002 HTTPS)
- 域名:www.fox360.cn 解析到路由器公网 IP,通过端口映射转发到内网对应机器
🧱 准备工作
1. 申请 Let's Encrypt 证书 (DNS-01 验证)
由于 80 端口未开放,采用 DNS 验证,域名托管于新网(Xinnet)。
sudo certbot certonly --manual --preferred-challenges dns -d www.fox360.cn -d fox360.cn
按提示添加两条 TXT 记录:_acme-challenge.www.fox360.cn 和 _acme-challenge.fox360.cn。生效后获得证书:/etc/letsencrypt/live/www.fox360.cn/
2. 路由器端口映射 (确认)
| 外部端口 | 内部IP | 内部端口 | 协议 |
|---|---|---|---|
| 5173 | 192.168.39.152 | 5173 | TCP |
| 3000 | 192.168.39.152 | 3000 | TCP |
| 9001 | 192.168.39.168 | 9001 | TCP |
| 9002 | 192.168.39.168 | 9002 | TCP |
⚙️ 前端 Vite 配置 HTTPS
将证书复制到前端项目根目录:
sudo cp /etc/letsencrypt/live/www.fox360.cn/fullchain.pem ~/file-manager-frontend/server.crt
sudo cp /etc/letsencrypt/live/www.fox360.cn/privkey.pem ~/file-manager-frontend/server.key
sudo chown $USER:$USER ~/file-manager-frontend/server.*
vite.config.js 关键修改:
import fs from 'fs'
import path from 'path'
export default defineConfig({
server: {
https: {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt')),
},
proxy: {
'/api': {
target: 'https://www.fox360.cn:3000', // 后端 HTTPS 地址
changeOrigin: true,
secure: false, // 自签名证书时设为 false
}
}
}
})
⚙️ 后端 NestJS 配置 HTTPS
修改 main.ts,添加 httpsOptions:
import * as fs from 'fs';
import * as path from 'path';
const httpsOptions = {
key: fs.readFileSync(path.join(__dirname, '..', 'server.key')),
cert: fs.readFileSync(path.join(__dirname, '..', 'server.crt')),
};
const app = await NestFactory.create(AppModule, { httpsOptions });
同时确保 .env 中的 FRONTEND_URL 和 MINIO_PUBLIC_URL 使用 HTTPS:
FRONTEND_URL=https://www.fox360.cn:5173
MINIO_PUBLIC_URL=https://www.fox360.cn:9002
⚙️ MinIO 集群 HTTPS 改造 (核心难点)
1. 调整 Docker Compose 环境变量
在 docker-compose.yml 中为每个 MinIO 节点添加:
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
MINIO_SERVER_URL: https://www.fox360.cn:9002
MINIO_BROWSER_REDIRECT_URL: https://www.fox360.cn:9001
MINIO_DOMAIN: www.fox360.cn
MINIO_PUBLIC_ENDPOINT: https://www.fox360.cn:9002
# 禁用 MinIO 自身的 CORS,由 Nginx 处理
MINIO_API_CORS_ALLOW_ORIGIN: "none"
重启集群:docker-compose down && docker-compose up -d
2. Nginx 反向代理配置(关键)
创建 nginx-8node.conf,要点:
- 监听 9001 (控制台) 和 9002 (API) 的 HTTPS,并代理到后端 upstream (minio-node-1:9000 等)
- 解决混合内容:添加 HTTP→HTTPS 重定向 server 块监听 9002
- 禁用缓冲:防止大文件下载不完整 (
proxy_buffering off;) - 传递正确 Host:
proxy_set_header Host $http_host;
# HTTP 重定向到 HTTPS (9002)
server {
listen 9002;
server_name www.fox360.cn;
return 301 https://$host:9002$request_uri;
}
# HTTPS API 9002
server {
listen 9002 ssl;
server_name www.fox360.cn;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
location / {
proxy_pass http://minio_api; # upstream 定义
proxy_set_header Host $http_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;
}
}
- ./ssl:/etc/nginx/ssl:ro3. 前端上传代码修复混合内容
后端生成的预签名 URL 为 HTTP,而页面是 HTTPS,浏览器会阻止。在 PresignedUploader.jsx 中添加协议替换:
const originalUrl = presignedData.url;
// 强制将 http:// 替换为 https://
const uploadUrl = originalUrl.startsWith('http://')
? originalUrl.replace('http://', 'https://')
: originalUrl;
🐞 调试过程中遇到的主要问题及解决
原因:新增的 HTTP 重定向 server 块误加了 ssl 参数,导致缺少证书。解决:确保 HTTP 块只有 listen 9002; 而不带 ssl。
原因:MinIO 内部使用 HTTP 生成预签名 URL,虽然设置了 MINIO_SERVER_URL=https,但控制台下载接口仍返回 HTTP。解决:通过 Nginx 将 9002 端口的 HTTP 请求 301 重定向到 HTTPS,并在前端上传代码中强制替换协议。
原因:预签名 URL 为 HTTP,被浏览器拦截。解决:前端代码替换协议(见上文)。
原因:后端生成预签名 URL 时使用的 Key 被截断(因特殊字符)。解决:确保 MinioService.getPresignedDownloadUrl 直接使用完整的 file.path,不做任何截断。添加详细日志验证。
原因:MinIO 旧版对中文文件名的时间解析有 bug,不影响业务功能(分享下载正常)。解决:可忽略或升级 MinIO 版本。
✅ 最终验证清单
- ✔️ 前端访问
https://www.fox360.cn:5173,正常加载,无混合内容警告 - ✔️ 后端健康检查
https://www.fox360.cn:3000/health返回 JSON - ✔️ MinIO 控制台
https://www.fox360.cn:9001可登录并查看文件 - ✔️ 上传文件:通过前端上传,成功存入 MinIO
- ✔️ 创建分享:生成短链,访问时自动重定向 HTTPS,文件可下载
- ✔️ 下载文件:点击分享链接,文件完整下载
- ✔️ 浏览器控制台无红色报错,Network 请求均为 HTTPS
🔄 后续维护建议
- 证书续期:手动模式需每 90 天重新执行
certbot renew并手动添加 TXT 记录。推荐改为 DNS API 自动续期(如新网 API + certbot-dns-xinnet 插件)。 - MinIO 升级:考虑升级到较新版本以修复控制台中文文件名下载 500 错误。
- 监控告警:添加证书过期监控(如 30 天预警)。
- 备份:定期备份证书文件和 Nginx 配置。
—— 技术总结由亲历者整理,致敬每一个不眠的调试夜晚 🌙