浏览器中的 JavaScript 获取本机 IP 地址,首先要区分“本机 IP”和“公网 IP”这两个概念。通常所说的本机 IP,可能是局域网中的 IPv4 地址,例如 192.168.1.100,也可能是互联网服务看到的公网 IP。由于浏览器受到安全沙箱和隐私策略限制,JavaScript 并不能像 Node.js 一样直接枚举操作系统的网络接口,因此不同场景需要采用不同实现方式。
下面整理三种常见方案,并分别说明适用场景、实现代码以及实际使用时需要注意的问题。
一、通过 WebRTC 获取局域网 IP
WebRTC 是浏览器提供的实时通信技术,除了音视频通话之外,还涉及网络连接建立和 ICE 候选地址收集。在部分浏览器环境中,可以通过 RTCPeerConnection 获取候选网络地址,从而尝试得到设备的局域网 IP。
一个典型实现如下:
JavaScriptfunction 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() 请求:
JavaScriptasync 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 自己读取网卡信息,而是让服务器告诉浏览器:“你的请求是从哪个公网地址过来的”。
如果使用自己的后端服务,也可以采用同样思路。
例如前端:
JavaScriptfetch("/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 获取逻辑放到服务器端处理。
假设前端页面调用:
JavaScriptasync 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 为例,如果服务直接暴露在公网环境中,可以通过:
JavaScriptconst clientIP = req.headers["x-forwarded-for"]?.split(",")[0].trim() || req.socket.remoteAddress; res.json({ ip: clientIP });
如果部署在 Nginx、CDN 或其他反向代理之后,则应该根据实际网络拓扑正确配置代理转发规则。
例如 Nginx 可以通过相关配置传递客户端地址:
Nginxlocation / { 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:
JavaScriptconsole.log(window.localIP);
或者:
JavaScriptnavigator.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。
因此,不应该使用下面这种方式识别用户:
JavaScriptconst 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:
JavaScriptasync 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">正在获取...
如果页面运行在自己的业务服务器上,这种“前端请求 + 后端返回”的模式通常更加可控。
而对于必须在浏览器端完成的特殊网络场景,可以研究:
JavaScriptRTCPeerConnection
但需要针对目标浏览器进行实际测试,并准备无法获取真实局域网 IP 的降级方案。
总体而言,JavaScript 获取本机 IP 并不存在一个适用于所有浏览器和所有网络环境的万能 API。局域网 IP、公网 IP、服务器看到的客户端 IP 本身就是三个不同层面的概念。选择实现方案时,应先明确业务到底需要哪一种地址,再结合浏览器隐私策略、NAT、代理、CDN 和 IPv6 环境进行设计。对于正式业务系统,优先采用后端获取客户端 IP 的方式;对于纯前端场景,则应根据实际需求选择 WebRTC 或公网 IP 查询服务,并做好获取失败和地址类型变化的兼容处理。