Compare commits

...

7 commits

Author SHA1 Message Date
Mike Cao
39c99997ce
Merge pull request #3608 from fauzora/fix/tracker-configuration
Some checks are pending
Node.js CI / build (mysql, 18.18) (push) Waiting to run
Node.js CI / build (postgresql, 18.18) (push) Waiting to run
fix(hash): improve URL normalization and handling in tracking functions
2025-09-16 22:44:51 -07:00
Mike Cao
aa398201be
Merge pull request #3613 from nickcmaynard/fix-ip-header-ordering
Fix ordering to allow X-Forwarded-For to be correctly managed by Cloudflare
2025-09-16 20:57:20 -07:00
Mike Cao
2826daad1a
Merge branch 'master' into fix-ip-header-ordering 2025-09-16 20:57:12 -07:00
Mike Cao
840e0314f5
Merge pull request #3619 from go-task/geolocation-netlify
fix(geo): parse netlify ip header by default: `x-nf-client-connection-ip`
2025-09-16 20:55:15 -07:00
Andrey Nering
bfcc822b40 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
2025-09-14 22:10:21 -03:00
Nick Maynard
2177256a2c Fix ordering to allow X-Forwarded-For to be correctly managed by Cloudflare 2025-09-08 23:05:22 +01:00
Chairil Fauzi Firmansyah
c5298d5d45 fix(hash): improve URL normalization and handling in tracking functions 2025-09-04 18:00:28 +07:00
3 changed files with 32 additions and 12 deletions

View file

@ -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);
});

View file

@ -333,17 +333,21 @@ 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',
'forwarded',
'x-appengine-user-ip',
'x-nf-client-connection-ip',
'x-real-ip',
];

View file

@ -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;