HyperDX 反向代理子路径部署指南:Nginx 与 Traefik 配置深度解析
2026/9/24 13:38:12 网站建设 项目流程
  • 可观测性
  • 云原生
  • 运维

【免费下载链接】hyperdx

Resolve production issues, fast. An open source observability platform unifying session replays, logs, metrics, traces and errors powered by ClickHouse and OpenTelemetry.

项目地址:https://gitcode.com/gh_mirrors/hy/hyperdx
点击查看免费下载

导读:当 HyperDX 需要部署在域名子路径下(如http://example.com/hyperdx)而非根路径时,需要一套完整的前端子路径路由方案。本文基于 HyperDX 仓库中的 proxy/README.md 及其附带的 Nginx、Traefik 配置,系统讲解HYPERDX_BASE_PATHNEXT_PUBLIC_HYPERDX_BASE_PATHFRONTEND_URL三个环境变量的作用与取值约束,逐行拆解两种反向代理的配置实现,并结合前端 Next.jsbasePath与 API 服务端源码,讲透"根路径重定向 → 路径重写 → 直接代理"三层路由逻辑。读完本文,你将能独立为任何现有 HyperDX 部署配置子路径反向代理,并理解其背后的原理。

背景:为什么 HyperDX 需要子路径代理配置

HyperDX 默认以独立应用形式运行于域名根路径,但生产环境中经常出现需要与其它服务共享域名的情况。此时应用无法占用根路径,必须挂载在子路径(subpath)下,例如:

  • http://example.com/hyperdx(应用入口)
  • http://example.com/hyperdx/api/...(API 路由)
  • http://example.com/hyperdx/_next/static/...(前端静态资源)

问题在于,HyperDX 前端(Next.js)内部会产生大量指向根路径的请求(如/api/.../_next/...),而 API 服务端也会生成包含完整 URL 的重定向、邮件链接与告警链接。如果只简单地把应用塞进子路径,这些请求会全部 404。为此,仓库在 proxy/ 目录下提供了两套开箱即用的反向代理配置,让应用代码"假装运行在根路径",由代理透明地完成子路径路由,无需修改任何前端业务代码

  • Nginx 配置模板
  • Traefik 配置

核心环境变量:三者的分工与约束

子路径部署的所有行为都由三个环境变量驱动,它们分别在代理层、前端应用层和 API 服务端各司其职,必须在部署时统一配置。

HYPERDX_BASE_PATHNEXT_PUBLIC_HYPERDX_BASE_PATH:必须相同的"双胞胎"

环境变量使用者作用
HYPERDX_BASE_PATH反向代理(Nginx / Traefik)控制路径路由与重写规则,决定代理如何匹配、改写请求
NEXT_PUBLIC_HYPERDX_BASE_PATHNext.js 应用通过basePath让前端生成正确的静态资源链接与 API 路由

两个变量必须设置为完全相同的值,否则会出现"代理把请求转到了子路径、前端却按根路径生成资源链接"之类的错位。

取值规则:

  • 非空值时必须以/开头,例如/hyperdx(这是 Nginxlocation块与 Traefik 路由规则解析的前提);
  • 若要从根路径提供服务,可以省略这两个变量,或显式设置为/

前端侧的实际消费点在 packages/app/next.config.mjs:

const basePath = process.env.NEXT_PUBLIC_HYPERDX_BASE_PATH; const nextConfig = { basePath: basePath, // ... };

basePath是 Next.js 官方支持的部署前缀选项,它会让所有页面路由、/_next静态资源请求自动带上该前缀。同时,前端运行时配置也定义在 packages/app/src/config.ts:

// Deployment path prefix, mirroring `basePath` in next.config.mjs. Needed // anywhere an absolute URL is built for something outside the browser to call: // `window.location.origin` alone drops the prefix, and the API is served under // the same one as the UI. export const BASE_PATH = process.env.NEXT_PUBLIC_HYPERDX_BASE_PATH ?? '';

从源码注释可以看出,前端代码在构建"给浏览器外部调用"的绝对 URL 时必须拼接BASE_PATH,因为仅靠window.location.origin会丢掉子路径前缀——这从侧面印证了代理层路径重写(见下文)的必要性。

FRONTEND_URL:API 服务端的"公共地址"

环境变量使用者作用
FRONTEND_URLAPI 服务端(packages/api生成带完整协议的绝对 URL,用于重定向、邮件链接、告警链接等

约束:

  • 必须是包含协议(httphttps)的完整 URL
  • 必须包含HYPERDX_BASE_PATH中定义的子路径

API 侧在 packages/api/src/config.ts 中读取:

const DEFAULT_FRONTEND_URL = env.HYPERDX_APP_PORT ? `http://localhost:${env.HYPERDX_APP_PORT}` : ''; export const FRONTEND_URL = (env.FRONTEND_URL || DEFAULT_FRONTEND_URL) as string; // ... export const FRONTEND_REDIRECT_BASE = IS_INLINE_API ? '' : FRONTEND_URL;

FRONTEND_URL的实际用途非常广泛,可以从仓库源码中看到多处真实调用:

  • 团队邀请链接:${config.FRONTEND_URL}/join-team?token=${token}(见 packages/api/src/controllers/team.ts);
  • MCP 工具返回的跳转 URL(如告警、看板、已保存搜索,见 packages/api/src/mcp/tools/alerts/getAlert.ts 等);
  • 会话 Cookie 的域名设置:API 启动时会把FRONTEND_URL解析出的hostname写入 session cookie,协议为https时还会启用cookie.secure(见 packages/api/src/api-app.ts):
app.set('trust proxy', 1); if (!config.IS_CI && config.FRONTEND_URL) { const feUrl = new URL(config.FRONTEND_URL); sess.cookie.domain = feUrl.hostname; if (feUrl.protocol === 'https:') { sess.cookie.secure = true; } }

因此在子路径部署时,若FRONTEND_URL漏掉了子路径或协议,生成的链接将无法直达前端。

示例.env配置

以本地开发环境、子路径/hyperdx、前端端口4040为例,三个变量的完整配置为:

HYPERDX_BASE_PATH=/hyperdx NEXT_PUBLIC_HYPERDX_BASE_PATH=/hyperdx FRONTEND_URL=http://localhost:4040/hyperdx

Nginx 配置逐行拆解

proxy/nginx/nginx.conf.template 是一份可直接用于 Nginx 的完整 server 配置模板,核心思路是"根据环境变量动态生成基础路径,再按三类请求分别处理"。完整内容如下:

upstream app { server 127.0.0.1:8080; } server { listen 4040; set $base_path "${HYPERDX_BASE_PATH}"; if ($base_path = "/") { set $base_path ""; } # Common proxy headers proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; 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; # Redirect root to base path, if a base path is set location = / { if ($base_path != "") { return 301 $base_path; } # If no base path, just proxy to the app proxy_pass http://app; } # This handles assets and api calls made to the root and rewrites them to include the base path location ~ ^(/api/|/_next/|/__ENV\.js$|/Icon32\.png$) { # Note: $request_uri includes the original full path including query string proxy_pass http://app$base_path$request_uri; } # Proxy requests that are already prefixed with the base path to the app location ${HYPERDX_BASE_PATH} { # The full request URI (e.g., /hyperdx/settings) is passed to the upstream proxy_pass http://app; } }

基础路径的归一化

set $base_path "${HYPERDX_BASE_PATH}"; if ($base_path = "/") { set $base_path ""; }

${HYPERDX_BASE_PATH}是 Nginx 环境变量展开语法,与.env中的HYPERDX_BASE_PATH对应。若其值为/(表示部署在根路径),则归一化为空字符串,使后续所有逻辑退化为"纯根路径直连",无需任何重写。location = /块内的if ($base_path != "")分支正是靠这个归一化结果决定是否重定向。

三层路由逻辑

第一层:根路径重定向(Root Redirect)

location = / { if ($base_path != "") { return 301 $base_path; } proxy_pass http://app; }

精确匹配/:若配置了子路径(如/hyperdx),则返回301永久重定向到该子路径,确保用户访问域名根路径时总能落到正确的应用入口;若未配置子路径($base_path为空),则直接把请求代理给应用。

第二层:根路径请求重写(Path Rewriting)

location ~ ^(/api/|/_next/|/__ENV\.js$|/Icon32\.png$) { proxy_pass http://app$base_path$request_uri; }

正则匹配四类前端代码中常见的根路径请求:

  • /api/...:前端发起的 API 调用;
  • /_next/...:Next.js 的静态构建资源(JS/CSS chunk);
  • /__ENV.jsnext-runtime-env运行时环境变量注入脚本(与next.config.mjs中的configureRuntimeEnv()配合,见 packages/app/next.config.mjs);
  • /Icon32.png:浏览器图标等根级静态文件。

命中后,proxy_pass的目标为http://app$base_path$request_uri,即把子路径前缀拼接到原始请求 URI 之前再转发。例如请求/_next/static/chunk.js会被改写成/hyperdx/_next/static/chunk.js后发给上游。注释特别说明$request_uri保留了完整的原始路径与查询字符串,因此重写不会丢失 query 参数。

第三层:子路径请求直接代理(Direct Proxy)

location ${HYPERDX_BASE_PATH} { proxy_pass http://app; }

location指令直接使用${HYPERDX_BASE_PATH}作为前缀匹配路径,凡已带正确子路径的请求(如/hyperdx/settings)都会命中此块,并原样转发给上游。此时无需再拼接前缀,因为 Next.js 已通过basePath知道如何处理这些路径。

需要注意的是,Nginx 的location匹配遵循最长前缀优先规则:对于/hyperdx/_next/...这类请求,同时符合第二层(正则)与第三层(前缀)的匹配条件,但 Nginx 正则匹配优先级更高,会先命中第二层——由于 URI 已带前缀,$base_path$request_uri拼接后依然得到正确的完整路径,两种匹配结果殊途同归。

Traefik 配置逐行拆解

Traefik 侧提供了两个文件:动态配置 proxy/traefik/config.yml 与静态入口配置 proxy/traefik/traefik.yml。后者定义了监听:4040web入口点,并通过 file provider 动态加载前者:

entryPoints: web: address: ':4040' providers: file: filename: /etc/traefik/dynamic/config.yml watch: true

config.yml用三个 router 完整复刻了 Nginx 的三层逻辑,并借助{{ env "HYPERDX_BASE_PATH" }}模板语法读取环境变量:

http: routers: # This handles the main app at the basepath app-router: entryPoints: - web rule: 'PathPrefix(`{{ env "HYPERDX_BASE_PATH" }}`)' service: app-service # This handles assets and api calls at the root and rewrites them assets-api-router: entryPoints: - web rule: 'PathPrefix(`/api`) || PathPrefix(`/_next`) || Path(`/__ENV.js`) || Path(`/Icon32.png`)' service: app-service middlewares: - add-basepath # This redirects from / to the basepath root-redirect: entryPoints: - web rule: 'Path(`/`)' service: app-service # service is required, but redirect will happen first middlewares: - redirect-to-basepath middlewares: add-basepath: addPrefix: prefix: '{{ env "HYPERDX_BASE_PATH" }}' redirect-to-basepath: redirectRegex: regex: '^/$' replacement: '{{ env "HYPERDX_BASE_PATH" }}' permanent: true services: app-service: loadBalancer: passHostHeader: true servers: - url: 'http://127.0.0.1:8080'

三个 router 与 Nginx 三层逻辑一一对应:

  1. app-routerPathPrefix匹配所有以子路径开头的请求,对应"直接代理"层;
  2. assets-api-router:匹配/api/_next前缀以及/__ENV.js/Icon32.png精确路径,并挂载add-basepathmiddleware,通过addPrefix为这些根路径请求自动加上子路径前缀,对应"路径重写"层;
  3. root-redirect:精确匹配/,挂载redirect-to-basepathmiddleware,用redirectRegex把根路径301重定向到子路径,对应"根路径重定向"层。

其中passHostHeader: true保证上游能收到原始的Host头,与 Nginx 模板中的proxy_set_header Host $host;作用一致。两个代理配置虽然语法完全不同,但路由语义完全对齐,方便团队根据既有基础设施二选一。

原理小结:一次完整的子路径请求生命周期

综合 proxy/README.md 的描述与两套配置的实现,一次典型的子路径部署请求流程如下:

  1. 根路径重定向:用户访问http://example.com/,代理返回301指向http://example.com/hyperdx,保证用户始终落在正确入口;
  2. 路径重写:前端在浏览器中加载后,向根路径发出/api/.../_next/...等请求,代理拦截并在转发前拼接子路径,例如/_next/static/chunk.js/hyperdx/_next/static/chunk.js
  3. 直接代理:已带正确子路径的请求(含被重写后的请求)被原样转发给 Next.js 应用,由basePath正确解析处理。

这样的设计让前端应用始终以"根路径"的方式开发与构建,子路径的复杂性被完全封装在代理层,切换部署形态(根路径 ↔ 子路径)时只需调整环境变量与代理配置,无需改动业务代码。

部署检查清单

结合仓库中的 docker-compose.yml(其中通过FRONTEND_URL: ${HYPERDX_APP_URL}:${HYPERDX_APP_PORT}注入前端地址)与 docker/hyperdx/entry.prod.sh(FRONTEND_URL的默认值逻辑),子路径部署建议按以下顺序核对:

  1. 三个变量取值一致HYPERDX_BASE_PATHNEXT_PUBLIC_HYPERDX_BASE_PATH必须相同,且以/开头;
  2. FRONTEND_URL完整:包含协议、域名、端口与子路径,如https://example.com/hyperdx
  3. 代理层指向正确的上游:Nginx 的upstream app与 Traefik 的app-service.servers.url都指向 Next.js 应用的实际监听地址(默认127.0.0.1:8080,端口与HYPERDX_APP_PORT保持一致);
  4. 端口对齐:代理监听4040,与HYPERDX_APP_PORT保持一致;
  5. 静态资源与 API 可访问:部署后分别验证/{base}/_next/static/.../{base}/api/.../{base}/__ENV.js是否返回 200;
  6. 回归根路径场景:若删掉HYPERDX_BASE_PATH或设为/,确认代理退化为纯根路径直连,无多余重写。

完成以上步骤后,HyperDX 即可稳定运行在任何域名子路径之下,与共享域名的其它服务和平共处。

  • 可观测性
  • 云原生
  • 运维

【免费下载链接】hyperdx

Resolve production issues, fast. An open source observability platform unifying session replays, logs, metrics, traces and errors powered by ClickHouse and OpenTelemetry.

项目地址:https://gitcode.com/gh_mirrors/hy/hyperdx
点击查看免费下载

相关推荐

上一篇:推荐使用CocoaMarkdown:高效且灵活的Markdown处理框架
下一篇:WeMod 每日限制卡住打 Boss 的你?Wand-Enhancer 本地打补丁免费解锁 Pro

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询