PS:由于奈斯猫nsmao的免费key用不了导致小板报失效,所以自己部署了一个。

缺点:中国大陆IP的位置获取没有nasmao那么精准....

右边是小板报效果。

部署测试:https://ip.oortaka.top/

准备工作:

1:cloudflare账号

2:托管在cloudflare的域名

创建Workers

1:登录cloudflare找到Workers 路由---管理Workers--创建应用程序,选择从Hello World!开始

2:名称随便填,点击部署。完成后点击编辑代码,把默认的代码全部清空,复制下面的IP位置代码全部粘贴进去,完后重新点击部署。

const CACHE_TTL = 86400;   // Worker 边缘缓存 1 天
const TIMEOUT_MS = 1000;   // ip-api 最多等待 1 秒
const CACHE_VERSION = "v3";

function browserHeaders() {
  return {
    "content-type": "application/json;charset=UTF-8",
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type",
    "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
    "Pragma": "no-cache",
    "Expires": "0"
  };
}

function edgeCacheHeaders() {
  return {
    "content-type": "application/json;charset=UTF-8",
    "Cache-Control": `public, max-age=${CACHE_TTL}`
  };
}

async function fetchWithTimeout(url, timeoutMs) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  try {
    return await fetch(url, {
      signal: controller.signal,
      headers: {
        "User-Agent": "Mozilla/5.0"
      }
    });
  } finally {
    clearTimeout(timer);
  }
}

function makeBrowserResponse(body) {
  return new Response(body, {
    headers: browserHeaders()
  });
}

export default {
  async fetch(request, env, ctx) {
    if (request.method === "OPTIONS") {
      return new Response(null, {
        status: 204,
        headers: browserHeaders()
      });
    }

    const clientIp =
      request.headers.get("cf-connecting-ip") ||
      request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
      "";

    if (!clientIp) {
      const body = JSON.stringify({
        status: "fail",
        code: 400,
        data: {
          ip: "",
          country: "未知",
          prov: "",
          city: "",
          district: "",
          lat: 0,
          lng: 0
        }
      });

      return new Response(body, {
        status: 400,
        headers: browserHeaders()
      });
    }

    const cache = caches.default;

    // 注意:缓存 key 里包含 IP,所以不同 IP 不会共用缓存
    const cacheKey = new Request(
      `https://ip-cache.local/${encodeURIComponent(clientIp)}?v=${CACHE_VERSION}`,
      { method: "GET" }
    );

    const cached = await cache.match(cacheKey);

    if (cached) {
      const cachedBody = await cached.text();

      // 这里不能直接 return cached,
      // 否则浏览器又会缓存旧结果
      return makeBrowserResponse(cachedBody);
    }

    let result;

    try {
      const ipApiRes = await fetchWithTimeout(
        `http://ip-api.com/json/${encodeURIComponent(clientIp)}?lang=zh-CN`,
        TIMEOUT_MS
      );

      const d = await ipApiRes.json();

      result = {
        status: "success",
        code: 200,
        data: {
          ip: clientIp,
          country: d.country || "中国",
          prov: d.regionName || "",
          city: d.city || "",
          district: d.district || "",
          lat: d.lat || 0,
          lng: d.lon || 0
        }
      };
    } catch (e) {
      const cf = request.cf || {};

      result = {
        status: "success",
        code: 200,
        data: {
          ip: clientIp,
          country: cf.country || "中国",
          prov: cf.region || "",
          city: cf.city || "",
          district: "",
          lat: cf.latitude ? Number(cf.latitude) : 0,
          lng: cf.longitude ? Number(cf.longitude) : 0
        }
      };
    }

    const body = JSON.stringify(result);

    // 存进 Worker 边缘缓存
    const edgeResponse = new Response(body, {
      headers: edgeCacheHeaders()
    });

    ctx.waitUntil(cache.put(cacheKey, edgeResponse.clone()));

    // 返回给浏览器时禁止浏览器缓存
    return makeBrowserResponse(body);
  }
};

3:部署成功后,点击设置--添加域--自定义域,输入自己喜欢的二级域名,完成后直接输入域名就可以返回IP信息了。