【Web全栈进阶】Nginx反向代理 + HTTPS:正式上线

前端build成静态产物、Nginx反向代理API、Let's Encrypt免费证书------从"localhost能访问"到"全世界能访问"。

🎯 本篇产出:前端生产Dockerfile(多阶段构建)+ nginx.conf + compose调整 + HTTPS上线流程。含代码约100行。


📌 太长不看版(给想快速上手的你)

项目信息 一句话说明
本篇目标 把早报站正式发布到公网
代码行数 ~100行(Dockerfile + nginx.conf + compose)
依赖 nginx:1.30-alpine + certbot
核心功能 多阶段构建 + 反向代理 + SPA兜底 + HTTPS
跑起来的命令 域名访问 → 浏览器锁形图标
核心知识点 反向代理、try_files、proxy_pass、Let's Encrypt
做完你能得到 一个全世界可访问、带HTTPS的早报站

一、概念课:Nginx是什么,反向代理在代理什么

Nginx :高性能Web服务器 + 反向代理。两件事都干:静态文件直接发 (比uvicorn快、省心)、动态请求转给别人(后端、API)。

🔄 正向代理 vs 反向代理

类型 代理谁 例子
正向代理 代"你"出去访问别人 翻墙软件
反向代理 代"服务器"接待所有进来的请求 一个前台接待:所有请求先到Nginx,它按规则分发给对应的服务

🎯 为什么上线离不开它

text 复制代码
浏览器 → https://daily.example.com
            ↓ 443
        Nginx(证书终结 + 静态文件 + 路由)
         ├─ /          → dist/ 静态产物(前端)
         └─ /api/      → 转发 backend:8000(FastAPI)
            └─ /api/images/ → 走文件流(FileResponse已实现)
# 理由 说明
① 统一入口 浏览器只需记一个域名
② HTTPS只在一处配置 证书放Nginx即可
③ 前后端同源 反代后/api和页面同域名------CORS问题自动消失

二、第0步:前端生产构建------多阶段Dockerfile

之前的前端是dev容器(npm run dev),上线要的是build后静态产物 。frontend/Dockerfile升级:

dockerfile 复制代码
# 阶段一:构建(装依赖 → 打包)
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
RUN npm run build                # 产出dist/

# 阶段二:运行(只要产物 + Nginx)
FROM nginx:1.30-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html

📌 多阶段构建的设计

🎯 构建工具(node、npm、几千个依赖)只存在于阶段一,最终镜像只带Nginx + dist产物------生产镜像瘦身20倍以上,攻击面也小得多。

🆕 镜像版本选择

镜像 当前状态 选型理由
node:24-alpine Active LTS(Krypton) Node 20已EOL,Node 26是Current但2026年10月28日才进入Active LTS,生产环境用24最稳
nginx:1.30-alpine 当前稳定分支(1.30.6) 2026年4月替代1.28成为生产推荐分支,alpine变体约27MB

💡 生产环境建议固定小版本标签(如nginx:1.30.6-alpine),避免意外升级。


三、第1步:nginx.conf------静态 + 反代 + SPA兜底

frontend/nginx.conf:

nginx 复制代码
server {
    listen 80;
    server_name daily.example.com;          # 换成你的域名

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

    # 前端静态文件 + SPA路由兜底
    location / {
        try_files $uri $uri/ /index.html;   # ① 刷新任意路由不404
    }

    # API反向代理
    location /api/ {
        proxy_pass http://backend:8000;     # ② 容器网络内转发(服务名)
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

📌 两个细节决定成败

# 细节 说明
① try_files兜底 Vue Router用history模式,直接刷新/favorites时Nginx找不到文件会404------try_files ... /index.html把未知路径全部交给Vue路由接管。这就是"本地好好的、上线刷新就404"的幕后黑手
② proxy_pass不带路径 location /api/ + proxy_pass http://backend:8000(结尾无斜杠)→ 请求/api/articles原样转发为backend的/api/articles------否则路径会被截断(第八节②)

四、第2步:compose调整------只有80对外

compose.yaml 改动两处:

yaml 复制代码
  backend:
    ...
    expose: ["8000"]        # ① 不再对外映射,只有内部网络可达
    # ports: ["8000:8000"]  ← 删掉这行(生产环境)

  frontend:
    build:                  # ② 自动用新的生产Dockerfile
      context: ./daily-front
      dockerfile: ../frontend/Dockerfile      
    ports: ["80:80"]        # ③ 唯一对外入口
    depends_on: [backend]

🚨 生产环境只暴露80/443------后端端口对外不可见,从网络层就少一个被攻击的面("最小暴露")。


五、第3步:HTTPS------Let's Encrypt免费证书

前提 :域名解析(DNS A记录 → 服务器公网IP,dig daily.example.com查一下)。

📜 签发证书(三条命令)

bash 复制代码
# 服务器上(先起好Nginx反代,80可访问)
apt install certbot python3-certbot-nginx
certbot --nginx -d daily.example.com -d www.daily.example.com   # 自动改Nginx并签证书
certbot renew --dry-run                                          # 验证自动续期

📌 Let's Encrypt三件事

# 说明
① 证书90天有效
② certbot自动续期(systemd timer)
③ 续期后自动reload Nginx------基本零维护

🎯 金句回扣

反代之后,前端和API同域名同端口(https://daily.example.com与https://daily.example.com/api)------浏览器的"同源"判定满足,CORS配置从"必须"变成"不再需要"(本地开发5173→8000跨端口仍然要)。
💡 这就是架构改善一劳永逸的例子:搞明白原理,以后再见面就是老朋友。

🛡️ 进阶:TLS加固(可选,但推荐)

Certbot生成的配置已经够用,想更安全可以加几条:

nginx 复制代码
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
server_tokens off;

六、第4步:上线前安全检查

text 复制代码
□ SECRET_KEY换成强随机(openssl rand -hex 32,走secrets)
□ 上传目录图片可访问(/api/images/走反代)
□ 生产环境debug=False(默认已是)
□ PG数据定时备份(pg_dump cron,或先手动做一次)
□ 服务器防火墙只开22/80/443

七、验收清单

bash 复制代码
1. 浏览器输入域名 → 首页打开(HTTPS锁形图标)
2. 刷新/favorites等前端路由 → 不404(SPA兜底生效)
3. 登录流程全通:API走https://同域名/api/
4. http://访问 → 自动跳https(练习③后)
5. 上传一张图片 → https下正常显示(第16篇的UGC上线)
6. 服务器上docker compose ps:frontend(80) / backend(内部) / db / redis 全Running
7. 全程无报错后提交Git
bash 复制代码
git add .
git commit -m "生产部署:Nginx反代 + HTTPS + 多阶段构建"

八、常见报错:这6个,上线日的标配(重点!)

① 刷新前端路由404(首页正常)

🔍 原因 :try_files兜底没配------Nginx找/favorites文件找不到就404。

✅ 解法 :location /里加try_files $uri $uri/ /index.html;(第3节写法)。


② API全部502 Bad Gateway

🔍 原因 :proxy_pass目标不对------写成了localhost:8000(Nginx容器里没有backend服务)或服务名拼错。

✅ 解法 :容器网络内用服务名backend:8000;先docker compose ps确认backend在跑。


③ 上传的图片打不开(其他功能正常)

🔍 原因 :图片URL是/api/images/xxx------没走/api/的转发?或location顺序/路径配置有误。

✅ 解法 :图片URL本身带/api/前缀,会命中/api/ location转给后端(第16篇的FileResponse兜底);检查proxy_pass是否截断了路径。


④ certbot报"域名无法解析"

🔍 原因:DNS A记录没生效、IP填错、或还没等DNS传播。

✅ 解法 :dig daily.example.com确认返回服务器IP;改DNS后等几分钟再试。


⑤ HTTPS证书无效 / 浏览器报警

🔍 原因:证书域名不匹配(www和非www只签了一个)、续期失败。

✅ 解法 :certbot --nginx -d daily.example.com -d www.daily.example.com两个域名都签;certbot renew --dry-run验证续期。


⑥ Nginx起不来 / 容器内解析不到backend

🔍 原因:frontend容器和backend不在同一compose网络,或backend服务名写错。

✅ 解法 :compose默认同一网络(服务名互访);确认/api/的proxy_pass与compose.yaml里服务名逐字一致。


九、课后练习

# 练习 难度 提示
1 上传目录安全 :给Nginx加一条location /api/images/ { }禁执行策略 ⭐⭐ 第16篇的"存储目录不执行脚本"落地
2 gzip/brotli压缩:静态资源开启压缩 ⭐⭐ Nginx一行配置,JS/CSS体积降60%+
3 80→443强制跳转 :certbot生成的配置里加return 301 https://$host$request_uri; ⭐ 自动配置一般已包含,检查确认即可
4(选做) 自定义404页 :error_page 404 /index.html ⭐⭐ SPA项目里"错误页也是前端的事"

📦 配套代码

生产Dockerfile、nginx.conf与compose调整已上传Git(python_daily/):【gitee仓库地址】

相关推荐
全栈弄潮儿1 小时前
Python实战第11期:面向对象编程基础
后端·python·编程语言
vivo互联网技术1 小时前
域名埋点零侵入实时校验实践:基于代理转发与规则驱动的工具设计
前端·测试·监控·代理·埋点
喵个咪1 小时前
GoWind ShortPlay 拆解:Flutter 沉浸式 Feed 的播放器工程——生命周期、手势状态机与游客降级
前端·flutter·架构
白日做梦Q1 小时前
Chrome商店访问不了,无法现在安装插件?利用Edge商店迁移扩展完整实操教程问题背景
前端·chrome·edge
估值探索者1 小时前
【Python量化技术指标实战 02】MACD金叉也分强弱?手工算一次 DIF/DEA 就懂了
java·开发语言·python·股票数据·股票数据api接口
涛涛ing1 小时前
2026年,你的前端终于不用再“算随机数”和“量高度”了
前端
用户298698530141 小时前
React 前端 Word 转图片:页面与元素级导出的工程实践
前端·javascript·react.js
2501_916008891 小时前
马甲包过审卡在哪?Ipa Guard 能做的和做不到的
android·小程序·https·uni-app·iphone·webview
魔都吴所谓1 小时前
【我的开源】Python实现一个离线 PDF 工具箱:架构、骑缝章与踩坑记录
python·pdf·开源