JavaScript获取本机IP地址的三种实现方案

8 次阅读

浏览器中的 JavaScript 获取本机 IP 地址,首先要区分“本机 IP”和“公网 IP”这两个概念。通常所说的本机 IP,可能是局域网中的 IPv4 地址,例如 192.168.1.100,也可能是互联网服务看到的公网 IP。由于浏览器受到安全沙箱和隐私策略限制,JavaScript 并不能像 Node.js 一样直接枚举操作系统的网络接口,因此不同场景需要采用不同实现方式。

下面整理三种常见方案,并分别说明适用场景、实现代码以及实际使用时需要注意的问题。

一、通过 WebRTC 获取局域网 IP

WebRTC 是浏览器提供的实时通信技术,除了音视频通话之外,还涉及网络连接建立和 ICE 候选地址收集。在部分浏览器环境中,可以通过 RTCPeerConnection 获取候选网络地址,从而尝试得到设备的局域网 IP。

一个典型实现如下:

JavaScript
function getLocalIP() {
    return new Promise((resolve, reject) => {
        const pc = new RTCPeerConnection({
            iceServers: []
        });

        pc.createDataChannel("");

        pc.onicecandidate = (event) => {
            if (!event.candidate) {
                pc.close();
                return;
            }

            const candidate = event.candidate.candidate;
            const match = candidate.match(
                /([0-9]{1,3}(?:.[0-9]{1,3}){3})/
            );

            if (match) {
                const ip = match[1];

                if (
                    ip.startsWith("192.168.") ||
                    ip.startsWith("10.") ||
                    /^172.(1[6-9]|2d|3[0-1])./.test(ip)
                ) {
                    resolve(ip);
                    pc.close();
                }
            }
        };

        pc.createOffer()
            .then(offer => pc.setLocalDescription(offer))
            .catch(reject);
    });
}

getLocalIP()
    .then(ip => {
        console.log("局域网 IP:", ip);
    })
    .catch(error => {
        console.error("获取 IP 失败:", error);
    });

这里判断了三个常见的 IPv4 私有地址范围:

10.0.0.0/8
172.16.0.0/12
192.168.0.0/16

例如设备连接家庭路由器后,可能获得:

192.168.1.105

这种地址属于局域网 IP。

不过,WebRTC 获取 IP 的方式并不是完全可靠。现代浏览器越来越重视用户隐私,可能对 ICE 候选地址进行隐藏、代理或者使用 mDNS 主机名替代真实局域网 IP。因此,即使代码本身没有问题,也不能保证所有浏览器都能得到真实的局域网地址。

如果项目只是需要识别设备所在网络,不能把 WebRTC 获取 IP 当成绝对可靠的身份识别方案。

二、通过公网 IP 查询服务获取外网 IP

如果真正需要的是“当前设备访问互联网时使用的公网 IP”,WebRTC 并不是最合适的解决方案。

更常见的方式是调用一个能够返回客户端公网 IP 的 HTTP 服务。

例如服务返回:

JSON
{
    "ip": "203.0.113.25"
}

前端 JavaScript 可以直接使用 fetch() 请求:

JavaScript
async function getPublicIP() {
    try {
        const response = await fetch("https://api64.ipify.org?format=json");

        if (!response.ok) {
            throw new Error("请求失败");
        }

        const data = await response.json();

        console.log("公网 IP:", data.ip);
        return data.ip;
    } catch (error) {
        console.error("获取公网 IP 失败:", error);
        return null;
    }
}

getPublicIP();

这种方法的核心并不是 JavaScript 自己读取网卡信息,而是让服务器告诉浏览器:“你的请求是从哪个公网地址过来的”。

如果使用自己的后端服务,也可以采用同样思路。

例如前端:

JavaScript
fetch("/api/client-ip")
    .then(response => response.json())
    .then(data => {
        console.log("公网 IP:", data.ip);
    });

服务器端根据 HTTP 请求获取客户端地址,然后返回给前端。

这种架构通常比单纯依赖第三方 IP 查询接口更加适合生产项目,因为可以控制数据格式、访问策略、日志以及异常处理。

需要特别注意代理和 CDN 环境。

例如用户请求链路可能是:

浏览器
   ↓
CDN
   ↓
反向代理
   ↓
Web服务器

此时服务器直接读取 TCP 连接地址,拿到的可能是 CDN 或反向代理的 IP,而不是用户真实公网 IP。

因此后端通常需要结合可信代理设置处理 X-Forwarded-For、X-Real-IP 等请求头。但这些请求头本身可以被伪造,不能在没有可信代理链配置的情况下直接把它们当成可靠身份信息。

三、通过后端接口获取客户端 IP

对于企业应用、管理系统、登录系统等正式项目,推荐将 IP 获取逻辑放到服务器端处理。

假设前端页面调用:

JavaScript
async function getClientIP() {
    try {
        const response = await fetch("/api/client-ip");

        if (!response.ok) {
            throw new Error("接口请求失败");
        }

        const result = await response.json();

        console.log("客户端 IP:", result.ip);

        return result.ip;
    } catch (error) {
        console.error("获取客户端 IP 失败:", error);
        return null;
    }
}

getClientIP();

后端负责读取 HTTP 请求信息。

以 Node.js 为例,如果服务直接暴露在公网环境中,可以通过:

JavaScript
const clientIP =
    req.headers["x-forwarded-for"]?.split(",")[0].trim() ||
    req.socket.remoteAddress;

res.json({
    ip: clientIP
});

如果部署在 Nginx、CDN 或其他反向代理之后,则应该根据实际网络拓扑正确配置代理转发规则。

例如 Nginx 可以通过相关配置传递客户端地址:

Nginx
location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_pass http://127.0.0.1:3000;
}

这样,Node.js 等后端应用才能根据可信代理链获取更加准确的客户端地址。

这种方案最大的优势是兼容性好,而且不依赖浏览器是否允许 JavaScript 获取底层网络信息。

三种方案如何选择

三种方式解决的问题并不完全相同。

方案获取目标浏览器兼容性是否需要服务器适用场景
WebRTC尝试获取局域网 IP受浏览器隐私策略影响不一定局域网相关实验、特定 WebRTC 场景
公网 IP API获取公网 IP较好第三方服务前端展示公网 IP
后端接口获取请求来源 IP好需要生产系统、日志、风控、审计

如果需求是“页面显示用户当前公网 IP”,调用 IP 查询接口即可。

如果需求是“尝试获得电脑在局域网中的 IPv4 地址”,可以研究 WebRTC,但必须接受浏览器限制。

如果需求涉及登录审计、访问日志、风控、权限控制等业务,则应该由后端获取 IP,而不是依赖前端 JavaScript。

为什么浏览器不能直接获取本机 IP

很多开发者第一次处理这个问题时,会尝试寻找类似下面的 API:

JavaScript
console.log(window.localIP);

或者:

JavaScript
navigator.getLocalIP();

实际上,标准浏览器并没有提供这样的通用 API。

浏览器 JavaScript 运行在受限制的安全环境中。如果网页可以随意获取用户所有网卡、内网地址以及网络拓扑信息,就可能带来隐私和安全风险。

因此现代浏览器对网络信息进行了越来越严格的限制。

尤其需要注意,过去一些基于 WebRTC 的“获取真实局域网 IP”技巧,在新的浏览器版本中可能出现结果为空、返回 mDNS 地址或者只能获得经过处理的候选地址等情况。

所以实际开发时不能简单认为:

WebRTC = 一定可以获取本机 IP

更加准确的理解应该是:

WebRTC = 可以收集 ICE 网络候选信息

而是否能够从中获得用户真正的局域网 IP,需要由浏览器实现和隐私策略决定。

IPv4 与 IPv6 也需要考虑

如果项目涉及 IP 地址处理,不应该只考虑 IPv4。

IPv4 通常类似:

192.168.1.100

IPv6 则可能类似:

2001:db8::1234

使用简单正则表达式匹配 IPv4:

JavaScript
/d{1,3}(.d{1,3}){3}/

无法处理 IPv6。

因此,如果业务同时支持 IPv4 和 IPv6,就应该采用专门的 IP 地址解析逻辑,而不是只依赖字符串截取。

后端日志中还可能出现 IPv4-mapped IPv6 地址,例如:

::ffff:192.168.1.100

处理时需要根据具体框架进行标准化,否则可能导致同一个客户端被记录成不同格式。

获取到 IP 后不要直接当作用户身份

IP 地址可以用于网络诊断、访问日志、粗粒度风控等场景,但不适合作为唯一用户身份标识。

例如多个用户可能通过同一个 NAT 网关访问网站:

用户A ─┐
用户B ─┼→ 路由器/NAT → 公网IP
用户C ─┘

服务器看到的公网 IP 可能完全相同。

另一方面,同一个用户使用手机网络、家庭宽带、公司网络或者 VPN 时,也可能拥有不同的公网 IP。

因此,不应该使用下面这种方式识别用户:

JavaScript
const userId = clientIP;

更合理的方式是使用账号体系、Session、Cookie 或经过安全设计的设备标识,并将 IP 作为辅助信息。

常见问题排查

1. WebRTC 获取不到 IP 怎么办?

首先检查浏览器控制台是否存在 WebRTC 相关异常。

其次检查浏览器是否隐藏了本地候选地址。如果浏览器只提供 mDNS 主机名,而不是 IPv4 地址,那么前端代码无法简单通过正则表达式恢复真实 IP。

这种情况下,如果业务真正需要公网 IP,可以改用服务器接口方案。

2. 获取到的是 127.0.0.1

127.0.0.1 是 IPv4 回环地址,表示当前设备自身,并不代表局域网 IP。

如果程序返回:

127.0.0.1

通常需要继续检查 ICE 候选信息,而不是把它当成真实局域网地址。

3. 获取到的是 IPv6

这并不意味着程序出错。

如果设备和网络同时启用了 IPv4、IPv6,WebRTC 或服务器可能优先得到 IPv6 地址。

业务层应该明确要求支持哪一种地址类型,而不是简单判断“不是 IPv4 就是错误”。

4. 本地开发环境和生产环境结果不同

这是非常常见的情况。

开发环境可能是:

浏览器 → localhost → 开发服务器

生产环境则可能是:

浏览器 → CDN → Nginx → 应用服务器

两种环境的网络拓扑完全不同,因此获取到的 IP 也可能不同。

尤其是后端读取客户端 IP 时,必须检查反向代理配置。

实际开发中的推荐方案

如果只是做一个 IP 地址展示页面,可以采用公网 IP API:

JavaScript
async function showIP() {
    try {
        const response = await fetch("/api/client-ip");
        const data = await response.json();

        document.querySelector("#ip").textContent =
            data.ip || "未知";
    } catch (error) {
        document.querySelector("#ip").textContent =
            "获取失败";
    }
}

showIP();

HTML:

HTML
当前IP: id="ip">正在获取...

如果页面运行在自己的业务服务器上,这种“前端请求 + 后端返回”的模式通常更加可控。

而对于必须在浏览器端完成的特殊网络场景,可以研究:

JavaScript
RTCPeerConnection

但需要针对目标浏览器进行实际测试,并准备无法获取真实局域网 IP 的降级方案。

总体而言,JavaScript 获取本机 IP 并不存在一个适用于所有浏览器和所有网络环境的万能 API。局域网 IP、公网 IP、服务器看到的客户端 IP 本身就是三个不同层面的概念。选择实现方案时,应先明确业务到底需要哪一种地址,再结合浏览器隐私策略、NAT、代理、CDN 和 IPv6 环境进行设计。对于正式业务系统,优先采用后端获取客户端 IP 的方式;对于纯前端场景,则应根据实际需求选择 WebRTC 或公网 IP 查询服务,并做好获取失败和地址类型变化的兼容处理。