Create custom headers to use in `detect.ts` > `getLocation` to get the country when using proxy + Cloudflare
Describe the feature or enhancement
Hello,
I am currently self-hosting Umami, and I want to proxy the Umami traffic to bypass ad-blockers. I am also using Cloudflare and Next.js. My traffic follows this flow:
User -> Cloudflare -> Proxy (the same Next JS app) -> Cloudflare -> Umami
However, because of the proxy, I have all the events marked as happening in the country of the proxy instead of being the country of the user.
I tried to overcome the issue by using CLIENT_IP_HEADER in order to read the header containing the IP address of the client. But then I realize that to get the location of the user, you are using the Cloudflare headers:
cf-ipcountrycf-region-codecf-ipcity
As I am using Cloudflare everywhere in the flow, Cloudflare is resetting these headers, so no matter the IP address sent to you, you will mark the traffic belonging to the country of the proxy.
I saw that 4 years ago, you were using COUNTRY_IP_HEADER for only one commit:
https://github.com/umami-software/umami/blob/b1ced5f32ce149e9f6ed309478203c2b80363297/lib/request.js#L55-L58
So, would you be able to reintroduce these kind of mechanisms, so I can bypass the latest location of Cloudflare? Otherwise, I would need to use the Maxmind location, but I am not sure how accurate they are compared to Cloudflare. The function I am talking about is https://github.com/umami-software/umami/blob/60eaaaff60a38efb3c779a250742187ec9201944/src/lib/detect.ts#L90-L148
A solution could be to add in getLocation from src/lib/detect.ts this code (this rejected commit https://github.com/umami-software/umami/pull/3153):
export async function getLocation(ip: string = '', headers: Headers, hasPayloadIP: boolean) {
// Ignore local ips
if (await isLocalhost(ip)) {
return;
}
if (!hasPayloadIP && !process.env.SKIP_LOCATION_HEADERS) {
const customHeader = String(
process.env.CLIENT_IPCOUNTRY_HEADER
).toLowerCase();
if (customHeader !== "undefined" && req.headers[customHeader]) {
const regionHeader = String(
process.env.CLIENT_IPREGION_HEADER
).toLowerCase();
const cityHeader = String(process.env.CLIENT_IPCITY_HEADER).toLowerCase();
const country = req.headers[customHeader];
const subdivision1 = req.headers[regionHeader];
const city = req.headers[cityHeader];
return {
country,
subdivision1: getRegionCode(country, subdivision1),
city,
};
}
// Cloudflare headers
if (headers.get("cf-ipcountry")) {
// ....
}
// ....
}
}
Would it be possible to have some systems like that?
Also, at the moment, I set this environment variable SKIP_LOCATION_HEADERS to true in self-hosted Umami instance (could be great to document it).
Now, it seems to show the good country. However, I don't know if using Maxmind and GeoLite2-City.mmdb has a cost?
Since you would have to generate the custom headers anyways, how about manipulating the payload and sending country, region, and city fields?
Hi @mikecao ,
I am manipulating it, but both the proxy and the website are under Cloudflare. Thus Cloudflare is overriding the value of the country/city/region when at the proxy from my experimentation. I log this at the proxy level and they are correct (I am not using next rewrites as it was not working to get the good location.
Thus, I think the implementation made https://github.com/umami-software/umami/pull/3153 could solve the issue.
I thought you were extracting location from the IP address, but in fact you extract it from explicit headers, and those ones are changed by Cloudflare when proxied.
I tried Maxmind free DB, but I feel like it is inaccurate.
I don't know if this issue might be linked to the other issue you are trying to solve currently https://github.com/umami-software/umami/issues/3583
Hi @mikecao ,
Does my change make sense in your opinion?
No matter what I do, I cannot change the location of the user unless I set SKIP_LOCATION_HEADERS to true. But then Maxmind free DB is not giving accurate location.
And Cloudflare location headers have already been overwritten when passing the proxy.
Hello @mikecao , Do you think we can add a header to change to custom header then? This code solved the issue: https://github.com/umami-software/umami/pull/3153
This issue is stale because it has been open for 60 days with no activity.