From f8eb3c2b766943a2a3d0a99ffa950eb89d810db9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Thu, 9 Apr 2026 17:35:42 -0600 Subject: [PATCH] fix: swap stripPrefix and addPrefix middleware order in Traefik domain config When both stripPath and internalPath are configured, addPrefix was pushed before stripPrefix causing incorrect path rewriting (e.g. /app/v2/public/api instead of /app/v2/api). Traefik executes middlewares in array order, so stripPrefix must come first. Closes #4061 --- apps/dokploy/__test__/traefik/traefik.test.ts | 20 +++++++++++++++++++ packages/server/src/utils/traefik/domain.ts | 12 ++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index fe7d0ff9d..14d45f76c 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -424,6 +424,26 @@ test("Custom entrypoint with internalPath adds addprefix middleware", async () = expect(router.entryPoints).toEqual(["custom"]); }); +test("stripPath and internalPath together: stripprefix must come before addprefix", async () => { + const router = await createRouterConfig( + baseApp, + { + ...baseDomain, + path: "/public", + stripPath: true, + internalPath: "/app/v2", + }, + "web", + ); + + const stripIndex = router.middlewares?.indexOf("stripprefix--1") ?? -1; + const addIndex = router.middlewares?.indexOf("addprefix--1") ?? -1; + + expect(stripIndex).toBeGreaterThanOrEqual(0); + expect(addIndex).toBeGreaterThanOrEqual(0); + expect(stripIndex).toBeLessThan(addIndex); +}); + test("Custom entrypoint with https and custom cert resolver", async () => { const router = await createRouterConfig( baseApp, diff --git a/packages/server/src/utils/traefik/domain.ts b/packages/server/src/utils/traefik/domain.ts index c1745ddab..596758b33 100644 --- a/packages/server/src/utils/traefik/domain.ts +++ b/packages/server/src/utils/traefik/domain.ts @@ -151,16 +151,18 @@ export const createRouterConfig = async ( routerConfig.middlewares?.push("redirect-to-https"); } else { // Add path rewriting middleware if needed - if (internalPath && internalPath !== "/" && internalPath !== path) { - const pathMiddleware = `addprefix-${appName}-${uniqueConfigKey}`; - routerConfig.middlewares?.push(pathMiddleware); - } - + // stripPrefix must come before addPrefix so Traefik strips the + // public path first, then prepends the internal path. if (stripPath && path && path !== "/") { const stripMiddleware = `stripprefix-${appName}-${uniqueConfigKey}`; routerConfig.middlewares?.push(stripMiddleware); } + if (internalPath && internalPath !== "/" && internalPath !== path) { + const pathMiddleware = `addprefix-${appName}-${uniqueConfigKey}`; + routerConfig.middlewares?.push(pathMiddleware); + } + // redirects - skip for preview deployments as wildcard subdomains // should not inherit parent redirect rules (e.g., www-redirect) if (domain.domainType !== "preview") {