🔐 全站 HTTPS 迁移实战总结

文件分享系统 · 从 HTTP 到 HTTPS 的完整蜕变
V1.0 2026年3月15日 · 迁移耗时 1 天

🎯 目标:将运行在 152 机器上的前端 (Vite) + 后端 (NestJS) 以及 168 机器上的 MinIO 集群 + Nginx 代理,全部升级为 HTTPS,并确保分享、上传、下载功能完整可用。

📌 网络拓扑与服务器角色

       公网入口 (路由器端口映射)
               │
       域名 www.fox360.cn
               │
        ┌──────┴──────┐
        │             │
     152机器         168机器
    ┌─────────┐    ┌──────────────────┐
    │ 前端5173│    │ MinIO 节点1-8     │
    │ 后端3000│    │   (9000内网)      │
    └─────────┘    │ Nginx 代理        │
                   │ 9001(控制台HTTPS) │
                   │ 9002(API HTTPS)   │
                   └──────────────────┘
                

🧱 准备工作

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/

⚠️ 注意:手动模式不会自动续期,后续需配置 hook 或改用 API 自动化。

2. 路由器端口映射 (确认)

外部端口内部IP内部端口协议
5173192.168.39.1525173TCP
3000192.168.39.1523000TCP
9001192.168.39.1689001TCP
9002192.168.39.1689002TCP

⚙️ 前端 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_URLMINIO_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,要点:

# 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:ro

3. 前端上传代码修复混合内容

后端生成的预签名 URL 为 HTTP,而页面是 HTTPS,浏览器会阻止。在 PresignedUploader.jsx 中添加协议替换:

const originalUrl = presignedData.url;
// 强制将 http:// 替换为 https://
const uploadUrl = originalUrl.startsWith('http://') 
    ? originalUrl.replace('http://', 'https://') 
    : originalUrl;

🐞 调试过程中遇到的主要问题及解决

❌ 问题1:Nginx 启动失败,提示 “no ssl_certificate defined for listen ... ssl”

原因:新增的 HTTP 重定向 server 块误加了 ssl 参数,导致缺少证书。解决:确保 HTTP 块只有 listen 9002; 而不带 ssl

❌ 问题2:MinIO 控制台生成的分享链接为 HTTP,下载失败

原因:MinIO 内部使用 HTTP 生成预签名 URL,虽然设置了 MINIO_SERVER_URL=https,但控制台下载接口仍返回 HTTP。解决:通过 Nginx 将 9002 端口的 HTTP 请求 301 重定向到 HTTPS,并在前端上传代码中强制替换协议。

❌ 问题3:上传报错 “Mixed Content”

原因:预签名 URL 为 HTTP,被浏览器拦截。解决:前端代码替换协议(见上文)。

❌ 问题4:下载文件时返回 403 Forbidden,或 NoSuchKey

原因:后端生成预签名 URL 时使用的 Key 被截断(因特殊字符)。解决:确保 MinioService.getPresignedDownloadUrl 直接使用完整的 file.path,不做任何截断。添加详细日志验证。

❌ 问题5:MinIO 控制台中文文件名下载 500 (Last-Modified time format invalid)

原因:MinIO 旧版对中文文件名的时间解析有 bug,不影响业务功能(分享下载正常)。解决:可忽略或升级 MinIO 版本。

✅ 最终验证清单

🎉 所有核心功能均已恢复,HTTPS 改造圆满完成!

🔄 后续维护建议


—— 技术总结由亲历者整理,致敬每一个不眠的调试夜晚 🌙