Reputation: 8404
There are many SO questions how to get http headers with javascript, but for some reason they don't show up HTTP_CF_IPCOUNTRY header.
If I try to do with php echo $_SERVER["HTTP_CF_IPCOUNTRY"];
, it works, so CF is working just fine.
Is it possible to get this header with javascript?
Upvotes: 16
Views: 27815
Reputation: 1574
You can get country code etc from cloudflares /cdn-cgi/trace
endpoint
const cloudflareFallbackURLs = ['https://one.one.one.one/cdn-cgi/trace',
'https://1.0.0.1/cdn-cgi/trace',
'https://cloudflare-dns.com/cdn-cgi/trace',
'https://cloudflare-eth.com/cdn-cgi/trace',
'https://cloudflare-ipfs.com/cdn-cgi/trace',
'https://workers.dev/cdn-cgi/trace',
'https://pages.dev/cdn-cgi/trace',
'https://cloudflare.tv/cdn-cgi/trace']
async function getCloudflareJSON(){
let data = await fetchWithFallback(cloudflareFallbackURLs).then(res=>res.text())
let arr = data.trim().split('\n').map(e=>e.split('='))
return Object.fromEntries(arr)
}
async function fetchWithFallback(links, obj) {
let response;
for (let link of links) {
try {
response = await fetch(link, obj)
if (response.ok)
return response
} catch (e) { }
}
return response
}
getCloudflareJSON().then(console.log)
Upvotes: 9
Reputation: 146010
Yes you have to hit the server - but it doesn't have to be YOUR server.
I have a shopping cart where pretty much everything is cached by Cloudflare - so I felt it would be stupid to go to MY server to get just the countrycode.
Instead I am using a webworker on Cloudflare (additional charges):
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
var countryCode = request.headers.get('CF-IPCountry');
return new Response(
JSON.stringify({ countryCode }),
{ headers: {
"Content-Type": "application/json"
}});
}
You can map this script to a route such as /api/countrycode
and then when your client makes an HTTP request it will return essentially instantly (for me it's about 10ms).
/api/countrycode
{
"countryCode": "US"
}
Couple additional things:
Since I wrote this they've exposed more properties on Request.cf
- even on lower priced plans:
https://developers.cloudflare.com/workers/runtime-apis/request#incomingrequestcfproperties
You can now get city
, region
and even longitude
and latitude
, without having to use a geo lookup database.
Upvotes: 7
Reputation: 94
I've taken Don Omondi's answer, and converted it to a promise function for ease of use.
function get_country_code() {
return new Promise((resolve, reject) => {
var trace = [];
jQuery.ajax('/cdn-cgi/trace', {
success: function(response) {
var lines = response.split('\n');
var keyValue;
for (var index = 0; index < lines.length; index++) {
const line = lines[index];
keyValue = line.split('=');
trace[keyValue[0]] = decodeURIComponent(keyValue[1] || '');
if (keyValue[0] === 'loc' && trace['loc'] !== 'XX') {
return resolve(trace['loc']);
}
}
},
error: function() {
return reject(trace);
}
});
});
}
usage example
get_country_code().then((country_code) => {
// do something with the variable country_code
}).catch((err) => {
// caught the error, now do something with it
});
Upvotes: 3
Reputation: 946
@Quentin's answer stands correct and holds true for any javascript client trying to access server header's.
However, since this question is specific to Cloudlfare and specific to getting the 2 letter country ISO normally in the HTTP_CF_IPCOUNTRY header, I believe I have a work-around that best befits the question asked.
Below is a code excerpt that I use on my frontend Ember App, sitting behind Cloudflare... and varnish... and fastboot...
function parseTrace(url){
let trace = [];
$.ajax(url,
{
success: function(response){
let lines = response.split('\n');
let keyValue;
lines.forEach(function(line){
keyValue = line.split('=');
trace[keyValue[0]] = decodeURIComponent(keyValue[1] || '');
if(keyValue[0] === 'loc' && trace['loc'] !== 'XX'){
alert(trace['loc']);
}
if(keyValue[0] === 'ip'){
alert(trace['ip']);
}
});
return trace;
},
error: function(){
return trace;
}
}
);
};
let cfTrace = parseTrace('/cdn-cgi/trace');
The performance is really really great, don't be afraid to call this function even before you call other APIs or functions. I have found it to be as quick or sometimes even quicker than retrieving static resources from Cloudflare's cache. You can run a profile on Pingdom to confirm this.
Upvotes: 12
Reputation: 943650
Assuming you are talking about client side JavaScript: no, it isn't possible.
The data never even goes near the browser.
For JavaScript to access it, you would need to read it with server side code and then put it in a response back to the browser.
Upvotes: 8