From c5298d5d45fa1ae8fd5488cc539a4f505ff75b7b Mon Sep 17 00:00:00 2001 From: Chairil Fauzi Firmansyah Date: Thu, 4 Sep 2025 18:00:28 +0700 Subject: [PATCH 1/3] fix(hash): improve URL normalization and handling in tracking functions --- src/tracker/index.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/tracker/index.js b/src/tracker/index.js index 76d29a1dd..b05d90859 100644 --- a/src/tracker/index.js +++ b/src/tracker/index.js @@ -38,6 +38,18 @@ /* Helper functions */ + const normalize = raw => { + if (!raw) return raw; + try { + const u = new URL(raw, location.href); + if (excludeSearch) u.search = ''; + if (excludeHash) u.hash = ''; + return u.toString(); + } catch (e) { + return raw; + } + }; + const getPayload = () => ({ website, screen, @@ -61,11 +73,7 @@ if (!url) return; currentRef = currentUrl; - currentUrl = new URL(url, location.href); - - if (excludeSearch) currentUrl.search = ''; - if (excludeHash) currentUrl.hash = ''; - currentUrl = currentUrl.toString(); + currentUrl = normalize(new URL(url, location.href).toString()); if (currentUrl !== currentRef) { setTimeout(track, delayDuration); @@ -210,8 +218,9 @@ }; } - let currentUrl = href; - let currentRef = referrer.startsWith(origin) ? '' : referrer; + let currentUrl = normalize(href); + let currentRef = normalize(referrer.startsWith(origin) ? '' : referrer); + let initialized = false; let disabled = false; let cache; From 2177256a2c537cdf9ab114efa74ccdc5c21320ed Mon Sep 17 00:00:00 2001 From: Nick Maynard Date: Mon, 8 Sep 2025 23:05:22 +0100 Subject: [PATCH 2/3] Fix ordering to allow X-Forwarded-For to be correctly managed by Cloudflare --- src/lib/__tests__/detect.test.ts | 7 +++++++ src/lib/constants.ts | 14 ++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/lib/__tests__/detect.test.ts b/src/lib/__tests__/detect.test.ts index 1cb558ad8..0ee345726 100644 --- a/src/lib/__tests__/detect.test.ts +++ b/src/lib/__tests__/detect.test.ts @@ -2,6 +2,7 @@ import * as detect from '../detect'; import { expect } from '@jest/globals'; const IP = '127.0.0.1'; +const BAD_IP = '127.127.127.127'; test('getIpAddress: Custom header', () => { process.env.CLIENT_IP_HEADER = 'x-custom-ip-header'; @@ -17,6 +18,12 @@ test('getIpAddress: Standard header', () => { expect(detect.getIpAddress(new Headers({ 'x-forwarded-for': IP }))).toEqual(IP); }); +test('getIpAddress: CloudFlare header is lower priority than standard header', () => { + expect( + detect.getIpAddress(new Headers({ 'cf-connecting-ip': BAD_IP, 'x-forwarded-for': IP })), + ).toEqual(IP); +}); + test('getIpAddress: No header', () => { expect(detect.getIpAddress(new Headers())).toEqual(null); }); diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 2718c135c..9c9ecd96c 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -333,18 +333,20 @@ export const BROWSERS = { yandexbrowser: 'Yandex', }; +// The order here is important and influences how IPs are detected by lib/detect.ts +// Please do not change the order unless you know exactly what you're doing - read https://developers.cloudflare.com/fundamentals/reference/http-headers/ export const IP_ADDRESS_HEADERS = [ - 'cf-connecting-ip', + 'x-client-ip', + 'x-forwarded-for', + 'cf-connecting-ip', // This should be *after* x-forwarded-for, so that x-forwarded-for is respected if present 'do-connecting-ip', 'fastly-client-ip', - 'forwarded', 'true-client-ip', - 'x-appengine-user-ip', - 'x-client-ip', + 'x-real-ip', 'x-cluster-client-ip', 'x-forwarded', - 'x-forwarded-for', - 'x-real-ip', + 'forwarded', + 'x-appengine-user-ip', ]; export const SOCIAL_DOMAINS = [ From bfcc822b4055f0f80212770407348de0ba62bc55 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Sun, 14 Sep 2025 22:10:21 -0300 Subject: [PATCH 3/3] fix(geo): parse netlify ip header by default: `x-nf-client-connection-ip` This header is not clearly documented, but it is mentioned in semi-official sources, and I have tested it to ensure it's working properly. Without this, Umami is unable to detect geolocation by default if deployed on Netlify. * https://answers.netlify.com/t/is-the-client-ip-header-going-to-be-supported-long-term/11203/2 * https://httptoolkit.com/blog/what-is-x-forwarded-for/#and-others --- src/lib/constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 2718c135c..18fc652b7 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -344,6 +344,7 @@ export const IP_ADDRESS_HEADERS = [ 'x-cluster-client-ip', 'x-forwarded', 'x-forwarded-for', + 'x-nf-client-connection-ip', 'x-real-ip', ];