全部文章

如何在 Node.js 中检测 VPN(分步指南)

一篇实用的 Node.js 教程:从后端调用 VPN 检测 API,读取判定结果和置信度分数,并据此控制注册或登录流程。

2026年5月19日阅读约 4 分钟

这是一篇关于如何在 Node.js 后端检测 VPN 流量的实用指南。我们会用 VPN 检测 API 查询访客的 IP,读取判定结果和置信度分数, 并据此控制注册流程。同样的模式也适用于登录和结账场景。

开始之前

你需要一个 API 密钥(免费套餐足以用于测试),以及 Node 18 及以上版本,以便使用全局的 fetch。请将密钥保存在环境变量中,切勿写入客户端代码。

先用任意 IP 测试一下查询效果

第一步:获取真实的客户端 IP

在代理或负载均衡器之后,req.socket.remoteAddress 得到的是你的基础设施地址,而不是用户 的真实地址。读取由你信任的代理设置的转发请求头:

function getClientIp(req) {
  const xff = req.headers['x-forwarded-for'];
  if (xff) return xff.split(',')[0].trim(); // left-most = original client
  return req.socket.remoteAddress;
}

只有在你自己掌控的代理设置了 X-Forwarded-For 时才信任它,否则客户端可能会伪造该请求头。

第二步:调用检测 API

将查询逻辑封装成一个小的辅助函数。它会返回判定结果和分数,并在网络出错时“失败开放” (默认放行),这样检测服务出问题时也不会把用户挡在门外:

const API_BASE = 'https://ipscanner.io';

async function checkVpn(ip) {
  try {
    const res = await fetch(`${API_BASE}/v1/vpn/${ip}`, {
      headers: { Authorization: `Bearer ${process.env.IPSCANNER_API_KEY}` }
    });
    if (!res.ok) throw new Error(`lookup failed: ${res.status}`);
    const data = await res.json();
    return { isVpn: Boolean(data.vpn), score: data.score ?? 0 };
  } catch (err) {
    console.error('vpn check error', err);
    return { isVpn: false, score: 0 }; // fail open
  }
}

第三步:根据结果控制注册流程

现在用这个分数来做决策:低于某个阈值就放行;处于中间区间就要求额外验证;分数较高则直接 拦截:

app.post('/signup', async (req, res) => {
  const ip = getClientIp(req);
  const { isVpn, score } = await checkVpn(ip);

  if (score >= 70) {
    return res.status(403).json({ error: 'Signups from this network need verification.' });
  }
  if (isVpn || score >= 30) {
    req.session.requireEmailVerification = true; // add friction, don't block
  }

  // ...create the account...
  res.json({ ok: true });
});

请注意,在中间区间我们是要求额外验证,而不是直接拦截。这样做的原因,可参阅 如何聪明地拦截 VPN 用户

第四步:使用缓存节省查询次数

同一个 IP 往往会反复出现。使用短期缓存可以减少 API 调用次数,同时不会让数据变得过时:

const cache = new Map(); // ip -> { value, expires }

async function checkVpnCached(ip, ttlMs = 10 * 60 * 1000) {
  const hit = cache.get(ip);
  if (hit && hit.expires > Date.now()) return hit.value;
  const value = await checkVpn(ip);
  cache.set(ip, { value, expires: Date.now() + ttlMs });
  return value;
}

请将缓存有效期设得短一些(以分钟为单位,而不是天),因为 IP 的风险会随时间变化。可参阅 什么是 IP 信誉

更进一步

  • 在同一流程中加入代理检测,这样不仅能识别 VPN,还能识别 代理和 Tor。
  • 将各类信号整合为一个统一的IP 欺诈分数,用这一个数值来做决策。
  • 将同样的模式应用到登录和结账场景,并根据不同操作调整阈值。

结论

在 Node.js 中检测 VPN,本质上就是一次服务器端的 API 调用:解析出真实的客户端 IP, 查询它,然后根据判定结果和分数采取行动。出错时默认放行,缓存时间保持较短,在中间风险 区间要求额外验证,而不是直接一刀切地拦截。

常见问题

常见问题解答

客户端检测容易被篡改,还会暴露你的 API 密钥。应始终从后端调用检测 API,这样结果才可信,凭证也能保持私密。

相关文章