By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.
The sanitize() function used to generate internal map keys for grouping targets in getTraefikConfig can cause path collisions. It replaces all non-alphanumeric characters (including /, ., _) with dashes and collapses them, so structurally different paths can produce the same key:
sanitize("/a/b") = "a-b"
sanitize("/a-b") = "a-b" (same!)
When two targets get the same key, they're grouped into one loadbalancer service and Traefik round-robins between them instead of routing by path.
Note: this specific collision doesn't affect the / vs /api scenario reported in the issue (those already produce different keys), but it would affect setups with paths like /api/v1 vs /api-v1 or /app.v2 vs /app/v2.
How to test?
Create two resources with paths that would collide under old sanitization (e.g., /api/v1 and /api-v1)
Verify they route to their correct backends independently (no round-robin)
Verify existing resources with simple paths still work with the same router names
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/fosrl/pangolin/pull/2524
**Author:** [@shreyaspapi](https://github.com/shreyaspapi)
**Created:** 2/23/2026
**Status:** ✅ Merged
**Merged:** 3/8/2026
**Merged by:** [@oschwartz10612](https://github.com/oschwartz10612)
**Base:** `dev` ← **Head:** `fix/2294-path-based-routing`
---
### 📝 Commits (4)
- [`5f18c06`](https://github.com/fosrl/pangolin/commit/5f18c06e03695b2567ccd802c830d6533d825773) fix: use collision-free path encoding for Traefik router key generation
- [`e58f0c9`](https://github.com/fosrl/pangolin/commit/e58f0c9f07cafe58b33e2f2b718709f528d1900c) fix: preserve backward-compatible router names while fixing path collisions
- [`244f497`](https://github.com/fosrl/pangolin/commit/244f497a9c0c0cbdd99ac6ae2336e65c51a3a88b) test: add comprehensive backward compatibility tests for path routing fix
- [`75a9097`](https://github.com/fosrl/pangolin/commit/75a909784af857e27de2de69af74aa4fdd4e80cd) fix: simplify path encoding per review — inline utils, use single key scheme
### 📊 Changes
**4 files changed** (+364 additions, -13 deletions)
<details>
<summary>View changed files</summary>
📝 `server/lib/traefik/getTraefikConfig.ts` (+9 -7)
➕ `server/lib/traefik/pathEncoding.test.ts` (+323 -0)
📝 `server/lib/traefik/utils.ts` (+20 -0)
📝 `server/private/lib/traefik/getTraefikConfig.ts` (+12 -6)
</details>
### 📄 Description
## Community Contribution License Agreement
By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.
## Description
Fixes #2294
## What this fixes
The `sanitize()` function used to generate internal map keys for grouping targets in `getTraefikConfig` can cause path collisions. It replaces all non-alphanumeric characters (including `/`, `.`, `_`) with dashes and collapses them, so structurally different paths can produce the same key:
- `sanitize("/a/b")` = `"a-b"`
- `sanitize("/a-b")` = `"a-b"` (same!)
When two targets get the same key, they're grouped into one loadbalancer service and Traefik round-robins between them instead of routing by path.
Note: this specific collision doesn't affect the `/` vs `/api` scenario reported in the issue (those already produce different keys), but it would affect setups with paths like `/api/v1` vs `/api-v1` or `/app.v2` vs `/app/v2`.
## How to test?
1. Create two resources with paths that would collide under old sanitization (e.g., `/api/v1` and `/api-v1`)
2. Verify they route to their correct backends independently (no round-robin)
3. Verify existing resources with simple paths still work with the same router names
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/fosrl/pangolin/pull/2524
Author: @shreyaspapi
Created: 2/23/2026
Status: ✅ Merged
Merged: 3/8/2026
Merged by: @oschwartz10612
Base:
dev← Head:fix/2294-path-based-routing📝 Commits (4)
5f18c06fix: use collision-free path encoding for Traefik router key generatione58f0c9fix: preserve backward-compatible router names while fixing path collisions244f497test: add comprehensive backward compatibility tests for path routing fix75a9097fix: simplify path encoding per review — inline utils, use single key scheme📊 Changes
4 files changed (+364 additions, -13 deletions)
View changed files
📝
server/lib/traefik/getTraefikConfig.ts(+9 -7)➕
server/lib/traefik/pathEncoding.test.ts(+323 -0)📝
server/lib/traefik/utils.ts(+20 -0)📝
server/private/lib/traefik/getTraefikConfig.ts(+12 -6)📄 Description
Community Contribution License Agreement
By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.
Description
Fixes #2294
What this fixes
The
sanitize()function used to generate internal map keys for grouping targets ingetTraefikConfigcan cause path collisions. It replaces all non-alphanumeric characters (including/,.,_) with dashes and collapses them, so structurally different paths can produce the same key:sanitize("/a/b")="a-b"sanitize("/a-b")="a-b"(same!)When two targets get the same key, they're grouped into one loadbalancer service and Traefik round-robins between them instead of routing by path.
Note: this specific collision doesn't affect the
/vs/apiscenario reported in the issue (those already produce different keys), but it would affect setups with paths like/api/v1vs/api-v1or/app.v2vs/app/v2.How to test?
/api/v1and/api-v1)🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.