使用JavaScript获取Cloudflare的HTTP_CF_IPCOUNTRY标头?

14
有许多SO问题涉及如何使用JavaScript获取HTTP标头,但因某些原因它们没有显示HTTP_CF_IPCOUNTRY标头。
如果我尝试使用php:echo $_SERVER["HTTP_CF_IPCOUNTRY"];,它可以工作,所以CF运行正常。
是否可能使用JavaScript获取此标头?

你指的是哪种JavaScript - 客户端还是服务器端(Node)? - hindmost
由于我的回答,我添加了Web Worker标签。希望你不介意 :-) - Simon_Weaver
您可以使用以下链接进行访问: https://cloudflare-quic.com/b/headers 或者 https://www.cloudflare.com/cdn-cgi/trace参考链接:https://cloudflare-quic.com/b 和 https://github.com/fawazahmed0/cloudflare-trace-api - Fawaz Ahmed
5个回答

12

@Quentin的回答是正确的,并适用于任何尝试访问服务器头部的javascript客户端。

然而,由于这个问题是特定于Cloudflare的,并且是特定于在HTTP_CF_IPCOUNTRY标头中获取2个字母的国家ISO,我相信我有一个解决方法最适合所提出的问题。

下面是一个代码��录,我在我的前端Ember App上使用,在Cloudflare后面...和varnish...和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');

这个函数的表现真的非常出色,不要担心在调用其他API或函数之前就调用它。我发现它比从Cloudflare的缓存中检索静态资源还要快,有时甚至更快。你可以在Pingdom上运行一下性能分析来确认这一点。


你能解释一下如何使用它吗?我应该在哪里声明我要定位的国家代码? - Michael Rogers

9

8

假设您讨论的是客户端JavaScript:不,这不可能。

  1. 浏览器向服务器发送HTTP请求。
  2. 服务器注意到请求来自哪个IP地址
  3. 服务器在数据库中查找该IP地址并找到相应的国家
  4. 服务器将该国家传递给PHP

数据甚至从未接近浏览器。

要使JavaScript访问它,您需要使用服务器端代码读取它,然后将其放入回传给浏览器的响应中。


抱歉,在问题的上下文中,这是误导性的。Cloudflare网络已经为您完成了这项工作(它知道人们在哪个国家),并将其传递到CF_IPCOUNTRY头中。因此,虽然您的方法是正确的,但步骤2和3应该只说“读取HTTP_CF_IPCOUNTRY头”。如果您尝试仅从IP查找国家,那么这将是一个很大的麻烦,并且可能会随时更改。Cloudflare的头应该更准确。 - Simon_Weaver
@Simon_Weaver — 在这种情况下,“服务器”指的是“Cloudflare服务器”(因为浏览器正在与该服务器通信)。事实上,第4步涉及从Cloudflare服务器向运行PHP的服务器发出额外的HTTP请求并不相关于问题(问题在于获取客户端信息)。 - Quentin

5

是的,您需要访问服务器 - 但不一定要访问您自己的服务器。

我有一个购物车,Cloudflare 缓存了几乎所有内容 - 所以我觉得单独为获取国家代码而访问我的服务器是愚蠢的。

相反,我在 Cloudflare 上使用了一个 Web Worker(额外收费):

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

您可以将此脚本映射到路由上,例如 /api/countrycode,当客户端发出HTTP请求时,它基本上会立即返回(对我来说大约是10毫秒)。

 /api/countrycode
 {
    "countryCode": "US"
 }

还有几件事情需要注意:

  • 在所有服务级别上均不能使用WebWorkers
  • 最好在同一URL上部署一个实际的WebService作为备份(如果WebWorkers未启用或不受支持或在开发期间)
  • 虽然会收取一些费用,但应该可以忽略不计
  • 似乎有一个新功能,可以将单个路径映射到单个脚本。这就是我在这里做的事情。我认为这曾经是一项专门面向企业的功能,但现在对我可用,这太棒了。
  • 不要忘记TOR网络可能是T1级别的

自从我写这篇文章以来,他们已经在更低价格的计划中公开了更多的Request.cf属性:

https://developers.cloudflare.com/workers/runtime-apis/request#incomingrequestcfproperties

现在,您甚至可以获取cityregion,甚至是longitudelatitude,而无需使用地理位置查找数据库。


重要提示:看起来当您有多个Web Workers时,它们不会按任何优先顺序运行。因此,如果您已经使用了此功能,则可能需要将此逻辑合并到您的主Web Worker脚本中。而且,如果您尚未使用Web Workers,您可能不想为此支付每月5美元! - Simon_Weaver
1
谢谢您,这对我们在 https://wiki.polkadot.network 上进行重定向非常有帮助。 - Swader
1
编辑队列已满,所以我会在这里发布我的评论。Cloudflare每天为您提供 100,000个免费的Web工作程序请求,您可以将其配置为在超过 100,000个请求后通过将请求传递给您的服务器来优雅地失败。因此,如果您的网站很少超过该数字,则这是一个绝佳的免费选择。@Simon_Weaver还未提到,使用XMLHttpRequest对象,您可以使用浏览器Javascript通过查询 /api/countrycode 或您设置的其他路由来查询国家。 - PHP Guru

3

我采用了Don Omondi的答案,并将其转换为promise函数以便于使用。

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

    });
}

使用示例

get_country_code().then((country_code) => {
    // do something with the variable country_code
}).catch((err) => {
    // caught the error, now do something with it
});

你能解释一下如何使用它吗?我应该在哪里声明我要定位的国家代码?我尝试了document.write(country_code);但是没有任何输出。 - Michael Rogers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接