在我们的内部仪表盘上使用 Nginx 反向代理设置 App Hub

发布: (2026年3月8日 GMT+8 19:10)
5 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的文章正文内容(包括任何需要保留的代码块、链接或其他 Markdown 元素),我将为您完整地翻译成简体中文并保持原有的格式。

背景

我们的团队运行一个基于 Flask 的单页应用(SPA)作为内部仪表盘。随着功能的增多,我们需要一种快速在服务之间跳转的方式,于是我们创建了 App Hub 面板。

此次更改:

  • 移除 3 个已退役的服务
  • 新增 3 项服务(TechsFree Shop、TechsFree ERP、Accounting AI)
  • 更新了已迁移服务的 URL

Problem: Host Header with IP‑Based Access

当在 App Hub 链接中通过 IP 地址引用另一内部服务器的服务时,Nginx 未能路由到正确的 vhost。

Nginx 配置假设基于子域名的路由(techsfree.comblog.techsfree.com 等)。当使用原始 IP 访问时,Host 头会变成 IP 本身,这与任何 server_name 都不匹配。

解决方案:向默认服务器添加 Location

# /www/server/panel/vhost/nginx/0.default.conf

server {
    listen 80 default_server;
    # ...

    location /shop/ {
        root /www/apps/techsfree-shop;
        try_files $uri $uri/ /shop/index.html;
    }

    location /erp/ {
        proxy_pass http://127.0.0.1:3101/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

关键点:default_server 虚拟主机添加基于路径的 location 块。当直接通过内部 IP(无域名)访问服务时,这是最简洁的做法。

静态文件 vs 反向代理

ServiceArchitectureNginx Config
TechsFree ShopVite‑构建的静态文件root 指令
TechsFree ERP后端 API + 前端proxy_pass

静态文件(Shop)

构建产物放置在 /www/apps/techsfree-shop/,由 Nginx 直接提供。由于是 SPA,try_files $uri $uri/ /shop/index.html 处理客户端路由。

反向代理(ERP)

后端运行在 Node.js(ts‑node)上,监听 3101 端口。Nginx 将请求转发给它。proxy_pass 中的结尾斜杠至关重要:

# 错误:/erp/ 前缀会传递给后端
location /erp/ {
    proxy_pass http://127.0.0.1:3101;   # 没有结尾斜杠
}

# 正确:/erp/ 前缀在转发前被剥除
location /erp/ {
    proxy_pass http://127.0.0.1:3101/;  # 有结尾斜杠
}

proxy_pass URL 中是否包含结尾斜杠会改变路径的处理方式。容易忽视,调试时令人恼火。

同时管理 systemd 与 PM2

另一个教训:混用进程管理器会导致混乱。

仪表盘在 Ubuntu 上使用 systemd 运行。我曾短暂尝试将其迁移到运行 aaPanel (PM2) 的服务器上,但遇到权限问题后又回滚了。

# Check on systemd server
systemctl --user status task-dashboard.service

# Check on PM2 server (if migrated)
pm2 status

当同一服务同时在两个环境中运行时,修改没有生效——因为 systemd 会生成新进程却没有终止旧的进程。

# Find stale processes
ps aux | grep server.py

# Kill explicitly, then restart
kill <pid>
systemctl --user restart task-dashboard.service

部署后务必检查是否存在重复的进程。

错误修复:JSON 字段名称不一致

在实现“批量删除已完成任务”功能时,我遇到了一个容易被忽视的错误。

任务的 JSON 使用 completed,但 API 的过滤逻辑却检查了 done

# Buggy
active_tasks = [t for t in tasks if not t.get('done')]

# Correct
active_tasks = [t for t in tasks if not t.get('completed')]

一旦确定了字段名称,就要在前端、后端和文档中保持一致。任何偏差都会导致类似的错误。

摘要

  • 向默认服务器添加 location 以处理基于 IP 的访问。
  • 根据使用场景选择静态文件或反向代理(单页应用需要 try_files)。
  • 注意 proxy_pass 中的尾随斜杠——它会改变路径处理方式。
  • 在混合使用进程管理器时,部署后检查是否有重复进程
  • 在整个技术栈中保持 JSON 字段名的一致性

即使是小型内部工具,在类似生产环境中运行时也能教会我们真实的经验教训。

Tags: nginx, reverse-proxy, spa, flask, systemd, deployment, webdev, infra

0 浏览
Back to Blog

相关文章

阅读更多 »