From a77d4d11a6eea8e97ea5efd3cddc4eac2500bdb2 Mon Sep 17 00:00:00 2001 From: NoiceHax Date: Thu, 13 Aug 2026 22:43:49 +0530 Subject: [PATCH 1/3] fix(send): keep the root path when REMOVE_TRAILING_SLASH is enabled The trailing-slash regex also matched the leading slash of the root URL, so with REMOVE_TRAILING_SLASH set every home page view was stored with an empty url_path: the home page vanished from the paths/entry/exit reports and could not be selected as a funnel step. Skip the match at the start of the path so "/" and "/#hash" are preserved while "/blog/" and "/blog/#hash" are still trimmed. Closes #4152 --- src/app/api/send/route.test.ts | 33 +++++++++++++++++++++++++++++++++ src/app/api/send/route.ts | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/app/api/send/route.test.ts b/src/app/api/send/route.test.ts index 1b1d3b75a..9733c0da2 100644 --- a/src/app/api/send/route.test.ts +++ b/src/app/api/send/route.test.ts @@ -482,6 +482,39 @@ describe('event url and referrer parsing', () => { expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/blog' }); }); + test('REMOVE_TRAILING_SLASH keeps the root path as "/"', async () => { + process.env.REMOVE_TRAILING_SLASH = '1'; + + await callPOST({ + type: 'event', + payload: { website: WEBSITE_ID, hostname: 'example.com', url: '/' }, + }); + + expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/' }); + }); + + test('REMOVE_TRAILING_SLASH keeps the root path when the url has a hash', async () => { + process.env.REMOVE_TRAILING_SLASH = '1'; + + await callPOST({ + type: 'event', + payload: { website: WEBSITE_ID, hostname: 'example.com', url: '/#hero' }, + }); + + expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/#hero' }); + }); + + test('REMOVE_TRAILING_SLASH strips a trailing slash before the hash', async () => { + process.env.REMOVE_TRAILING_SLASH = '1'; + + await callPOST({ + type: 'event', + payload: { website: WEBSITE_ID, hostname: 'example.com', url: '/blog/#hero' }, + }); + + expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/blog#hero' }); + }); + test('maps a "/undefined" pathname to an empty url path', async () => { await callPOST({ type: 'event', diff --git a/src/app/api/send/route.ts b/src/app/api/send/route.ts index 6da12076c..cd5d42634 100644 --- a/src/app/api/send/route.ts +++ b/src/app/api/send/route.ts @@ -214,7 +214,8 @@ export async function POST(request: Request) { const twclid = currentUrl.searchParams.get('twclid'); if (process.env.REMOVE_TRAILING_SLASH) { - urlPath = urlPath.replace(/\/(?=(#.*)?$)/, ''); + // Never strip the root slash, otherwise the home page is saved with an empty path + urlPath = urlPath.replace(/(?!^)\/(?=(#.*)?$)/, ''); } if (referrer) { From ac4e6c91607a93a28f5cd8fa8ca02fd7a4be93ac Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 20 Aug 2026 10:38:27 -0700 Subject: [PATCH 2/3] Bump version to v3.3.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 512a3f0d0..42fd50d03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "3.3.0", + "version": "3.3.1", "description": "A modern, privacy-focused alternative to Google Analytics.", "author": "Umami Software, Inc. ", "license": "MIT", From 4044986e8aadbeacd2c55dec0953d5b760929981 Mon Sep 17 00:00:00 2001 From: Murat Kirazkaya Date: Thu, 3 Sep 2026 16:27:43 +0300 Subject: [PATCH 3/3] refactor(website-menu): replace Waypoints icon with Link icon in expanded menu --- src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx index e3c333693..75efe96c5 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx @@ -11,6 +11,7 @@ import { Languages, Laptop, Layers, + Link, Link2, LogIn, LogOut, @@ -25,7 +26,6 @@ import { Tag, Target, Type, - Waypoints, } from '@/components/icons'; import { Lightning } from '@/components/svg'; @@ -58,7 +58,7 @@ export function WebsiteExpandedMenu({ id: 'fullPath', label: t(labels.url), path: updateParams({ view: 'fullPath' }), - icon: , + icon: , }, { id: 'entry',