Skip to content

Instantly share code, notes, and snippets.

@wyf9
Last active December 31, 2025 09:16
Show Gist options
  • Select an option

  • Save wyf9/beeb6746fa7fda73f78380cbdda5461e to your computer and use it in GitHub Desktop.

Select an option

Save wyf9/beeb6746fa7fda73f78380cbdda5461e to your computer and use it in GitHub Desktop.
CF Snippet GetIP API

一个简单的 Cloudflare Snippet 获取访问者 IP & 归属地 API 脚本

Try it: https://ip.siiway.top

Preview

preview

Important

Clash <-> Sharelink 转换功能已拆分为单独的仓库: siiway/urlclash-converter

路由

  • / -> 根目录 (根据访问客户端自动选择 HTML / 纯文本)
  • /json -> 返回 JSON 格式的数据
  • /txt -> 返回纯文本格式的数据
  • /html -> 返回网页格式的数据
  • /ip -> 只返回访问者 IP,不带任何格式 / 说明
  • /full -> 以 JSON 格式返回脚本获取到的所有信息 (IP, 归属地, Headers, Security, ...)

自行部署

请确保设置片段规则为 自定义筛选表达式

  • 字段: 主机名
  • 运算符: 等于
  • 值: 你想要的域名

切勿选择 所有传入请求, 否则会导致你整个域名的流量被路由到 snippet!

Example:

cf-cfg-1

Tip

如果出现提示 此规则可能不适用于您的流量, 只需要随指引创建一个对应的代理 A 记录,目标填写 192.0.2.1 即可

cf-cfg-2
function isBrowserUA(request) {
const userAgent = request.headers.get('User-Agent') || '';
const acceptHeader = request.headers.get('Accept') || '';
const prefersHtml = acceptHeader.includes('text/html');
const browserPatterns = [
'mozilla',
'chrome',
'safari',
'firefox',
'edge',
'opera',
'ucbrowser',
'samsungbrowser',
];
const nonBrowserPatterns = [
'curl',
'wget',
'httpie',
'postman',
'python-requests',
'bot',
'spider',
'crawler',
];
const isBrowserAgent = browserPatterns.some(pattern => userAgent.toLowerCase().includes(pattern));
const isNonBrowserAgent = nonBrowserPatterns.some(pattern => userAgent.toLowerCase().includes(pattern));
return prefersHtml || (isBrowserAgent && !isNonBrowserAgent);
}
async function process_json(request) {
let data = {
ip: request.headers.get('CF-Connecting-IP') || null,
cf: {
continent: request.cf?.continent || null,
country: request.cf?.country || null,
isEUCountry: request.cf?.isEUCountry || false,
region: request.cf?.region || null,
regionCode: request.cf?.regionCode || null,
city: request.cf?.city || null,
latitude: request.cf?.latitude || null,
longitude: request.cf?.longitude || null,
postalCode: request.cf?.postalCode || null,
metroCode: request.cf?.metroCode || null,
colo: request.cf?.colo || null,
asn: request.cf?.asn || null,
asOrganization: request.cf?.asOrganization || null,
timezone: request.cf?.timezone || null,
},
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
};
const dataJson = JSON.stringify(data, null, 4);
return new Response(dataJson, {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
});
}
async function process_txt(request) {
let data = {
ip: request.headers.get('CF-Connecting-IP') || null,
cf: {
continent: request.cf?.continent || null,
country: request.cf?.country || null,
region: request.cf?.region || null,
city: request.cf?.city || null,
colo: request.cf?.colo || null,
asn: request.cf?.asn || null,
asOrganization: request.cf?.asOrganization || null,
},
};
const text = `IP: ${data.ip || 'N/A'}\n` +
`Continent: ${data.cf.continent || 'N/A'}\n` +
`Country: ${data.cf.country || 'N/A'}\n` +
`Region: ${data.cf.region || 'N/A'}\n` +
`City: ${data.cf.city || 'N/A'}\n` +
`Colo: ${data.cf.colo || 'N/A'}\n` +
`ASN: ${data.cf.asn || 'N/A'}\n` +
`ASOrganization: ${data.cf.asOrganization || 'N/A'}\n` +
`Source: https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e\n`;
return new Response(text, {
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
});
}
async function process_html(request) {
let data = {
ip: request.headers.get('CF-Connecting-IP') || null,
cf: {
continent: request.cf?.continent || null,
country: request.cf?.country || null,
region: request.cf?.region || null,
city: request.cf?.city || null,
colo: request.cf?.colo || null,
asn: request.cf?.asn || null,
asOrganization: request.cf?.asOrganization || null,
},
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
};
const info = [];
if (data.ip) info.push(`<strong>IP:</strong> ${data.ip}`);
if (data.cf.continent) info.push(`<strong>大陆 | Continent:</strong> ${data.cf.continent}`);
if (data.cf.country) info.push(`<strong>国家 | Country:</strong> ${data.cf.country}`);
if (data.cf.region) info.push(`<strong>地区 | Region:</strong> ${data.cf.region}`);
if (data.cf.city) info.push(`<strong>城市 | City:</strong> ${data.cf.city}`);
if (data.cf.colo) info.push(`<strong>数据中心 | Colo:</strong> ${data.cf.colo}`);
if (data.cf.asn) info.push(`<strong>ASN:</strong> ${data.cf.asn}`);
if (data.cf.asOrganization) info.push(`<strong>AS 组织 | AS Organization:</strong> ${data.cf.asOrganization}`);
const visitorInfo = info.length > 0 ? info.join('<br/><br/>') : '没有可用的访客信息 | No visitor information available.';
const html = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IP 信息 | IP Information</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--bs-body-bg: #f8f9fa;
--bs-body-color: #212529;
}
@media (prefers-color-scheme: dark) {
:root {
--bs-body-bg: #212529;
--bs-body-color: #f8f9fa;
}
.card, .list-group-item {
--bs-card-bg: #2c3034;
--bs-card-color: #f8f9fa;
}
pre {
--bs-code-bg: #343a40;
}
}
body {
background-color: var(--bs-body-bg);
color: var(--bs-body-color);
}
.info-card .list-group-item {
background-color: transparent;
border-color: rgba(255,255,255,.125);
}
pre {
background-color: var(--bs-code-bg, #e9ecef);
border-radius: .375rem;
}
.source {
font-size: .875rem;
}
</style>
</head>
<body class="py-4">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<h1 class="mb-4 text-center">您的 IP 信息 | Your IP Information</h1>
<div class="card shadow-sm mb-4 info-card">
<div class="card-body">
<div class="list-group list-group-flush">
${info.map(item => `<div class="list-group-item px-0 py-3">${item}</div>`).join('') || '<div class="list-group-item px-0 py-3 text-muted">没有可用的访客信息 | No visitor information available.</div>'}
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-header">
<h2 class="h5 mb-0">原始数据 | Raw Data</h2>
</div>
<div class="card-body p-0">
<pre class="m-0 p-3 overflow-auto"><code>${JSON.stringify(data, null, 2)}</code></pre>
</div>
</div>
<p class="source text-center mt-4">
源代码 | Source: <a href="${data.source}" target="_blank" rel="noopener">${data.source}</a>
</p>
</div>
</div>
</div>
<script>
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
} else {
document.documentElement.setAttribute('data-bs-theme', 'light');
}
</script>
</body>
</html>
`;
return new Response(html, {
headers: {
'Content-Type': 'text/html;charset=UTF-8',
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
});
}
async function process_root(request) {
return isBrowserUA(request) ? process_html(request) : process_txt(request);
}
async function process_full(request, url) {
let data = {
method: request.method,
url: request.url,
origin: url.origin,
ip: request.headers.get('CF-Connecting-IP') || null,
cf: {
continent: request.cf?.continent || null,
country: request.cf?.country || null,
isEUCountry: request.cf?.isEUCountry || false,
region: request.cf?.region || null,
regionCode: request.cf?.regionCode || null,
city: request.cf?.city || null,
latitude: request.cf?.latitude || null,
longitude: request.cf?.longitude || null,
postalCode: request.cf?.postalCode || null,
metroCode: request.cf?.metroCode || null,
colo: request.cf?.colo || null,
asn: request.cf?.asn || null,
asOrganization: request.cf?.asOrganization || null,
timezone: request.cf?.timezone || null,
},
headers: {},
security: {},
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
};
// Populate Headers
request.headers.forEach((value, name) => {
data.headers[name] = value;
});
// Populate Security fields
const securityKeys = [
'clientTcpRtt',
'tlsCipher',
'tlsVersion',
'httpProtocol',
'clientHandshake',
'clientFinished',
'serverHandshake',
'serverFinished',
'corporateProxy',
'verifiedBot',
'score',
];
if (request.cf) {
for (const key of securityKeys) {
if (request.cf[key]) {
if (typeof request.cf[key] === 'object') {
for (const innerKey in request.cf[key]) {
data.security[innerKey] = request.cf[key][innerKey];
}
} else {
data.security[key] = request.cf[key];
}
}
}
}
const dataJson = JSON.stringify(data, null, 4);
return new Response(dataJson, {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
});
}
export default {
async fetch(request) {
const url = new URL(request.url);
switch (url.pathname) {
case '/ip':
case '/.ip':
return new Response(request.headers.get('CF-Connecting-IP') || 'N/A', {
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
});
case '/json':
case '/.json':
return await process_json(request);
case '/':
return await process_root(request);
case '/txt':
case '/.txt':
return await process_txt(request);
case '/html':
case '/.html':
return await process_html(request);
case '/full':
case '/.full':
return await process_full(request, url);
default:
return new Response(
JSON.stringify(
{
usage: {
'/ip': '返回纯 IP | Return only IP',
'/json': '返回 JSON 数据 (仅 IP 和位置信息) | Return JSON data (IP and location only)',
'/full': '返回完整数据 (包括头部和安全信息) | Return FULL data (includes headers & security)',
'/txt': '返回纯文本数据 (IP 和位置信息) | Return plain text data (IP and location)',
'/html': '返回 HTML 页面 (IP 和位置信息) | Return HTML with IP and location data',
},
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
null,
4
),
{
status: 404,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e',
},
}
);
}
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment