131

I know the initial reaction to this question is "no" and "it can't be done" and "you shouldn't need it, you are doing something wrong". What I'm trying to do is get the users LAN IP address, and display it on the web page. Why? Because that's what the page I'm working on is all about, showing as much information as possible about you, the visitor: https://www.whatsmyip.org/more-info-about-you/

So I'm not actually DOING anything with the IP, other than showing it to the user for informational purposes. I used to do this by using a small Java applet. It worked pretty well. But these days, browser make you hit agree and trust so many times, to run even the most minor java applet, that I'd rather not run one at all.

So for a while I just got rid of this feature, but I'd like it back if possible. It was something that I, as a computer consultant, would actually use from time to time. It's faster to go to this website to see what IP range a network is running on, than it is to go into System Preferences, Networking, and then whatever interface is active.

So I'm wondering, hoping, if there's some way to do it in javascript alone? Maybe some new object you can access, similar to the way javascript can ask the browser where is geographic location on earth is. Maybe theres something similar for client networking information? If not, perhaps theres some other way entirely to do it? The only ways I can think of are a java applet, or a flash object. I'd rather not do either of those.

6
  • 1
    You know the answer. Why asking then? Java applets or flash objects are unlikely to be allowed by users (may be only by those who're new in the Internet) - so it's not a solution in common case. ActiveX and nearby stuff is working only in IE - and, thus, users of other browsers will not be affected (and, more, even in IE there is a security policy which prevents web-site from doing nasty things)
    – Alma Do
    Commented Nov 25, 2013 at 13:55
  • My IP address is captured thru HTTP_X_FORWARDED_FOR on that page, just sayin`.
    – tomdemuyt
    Commented Nov 25, 2013 at 14:12
  • 81
    Why ask then? Because maybe, just maybe, I don't know everything.
    – l008com
    Commented Nov 26, 2013 at 0:18
  • 1
    These guys do it: whatismyproxy.com
    – likebike
    Commented Sep 29, 2019 at 11:32
  • 1
    @likebike Nice one. Looking into how they are doing this.
    – Birkensox
    Commented Nov 16, 2019 at 0:42

9 Answers 9

134

As it turns out, the recent WebRTC extension of HTML5 allows javascript to query the local client IP address. A proof of concept is available here: http://net.ipcalf.com

This feature is apparently by design, and is not a bug. However, given its controversial nature, I would be cautious about relying on this behaviour. Nevertheless, I think it perfectly and appropriately addresses your intended purpose (revealing to the user what their browser is leaking).

8
  • 7
    It is just working on the chrome and firefox, And NOT on the IE, Edge or safari
    – ali
    Commented Dec 16, 2016 at 0:24
  • 1
    I was looking up my WAN IP and this website whatismyip.com also gave me my local IP and I guess it had something to do with JS.
    – Shayan
    Commented Jul 13, 2019 at 14:21
  • @ali You're correct, the website I mentioned above is not able to tell my local IP on Edge.
    – Shayan
    Commented Jul 13, 2019 at 14:22
  • 17
    Google Chrome is hiding local IP by default. It shows something similar to e87e041d-15e1-4662-adad-7a6601fca9fb.local . This behaviour can be changes by setting the variable #enable-webrtc-hide-local-ips-with-mdns to disabled in Chrome://flags
    – injaon
    Commented Sep 11, 2019 at 14:51
  • 3
    This does not works any more, the object RTCSessionDescription return 0.0.0.0 as local ipV4 address. It was returning the local ip till about Firefox 80
    – NVRM
    Commented May 15, 2021 at 7:01
96

Update

This solution would not longer work because browsers are fixing webrtc leak: for more info on that read this other question: RTCIceCandidate no longer returning IP


In addition to afourney's answer this code works in browsers that support WebRTC (Chrome and Firefox). I heard there is a movement going on to implement a feature that makes sites request the IP (like in case of user's geo-location or user-media) though it has yet to be implemented in either of those browsers.

Here is a modified version of the source code, reduced the lines, not making any stun requests since you only want Local IP not the Public IP:

window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;//compatibility for Firefox and chrome
var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};      
pc.createDataChannel('');//create a bogus data channel
pc.createOffer(pc.setLocalDescription.bind(pc), noop);// create offer and set local description
pc.onicecandidate = function(ice)
{
 if (ice && ice.candidate && ice.candidate.candidate)
 {
  var myIP = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];
  console.log('my IP: ', myIP);   
  pc.onicecandidate = noop;
 }
};

We are creating a dummy peer connection for the remote peer to contact us. We generally exchange ice candidates with each other and reading the ice candidates we can tell the ip of the user.

You can find a demo at --> Demo

5
  • 1
    @dampee - I believe Edge does not support data channels at the moment.
    – MichaelB76
    Commented Apr 20, 2016 at 10:28
  • What is a bogus data channel? Can't find any reference on google Commented Jun 5, 2019 at 14:39
  • 2
    note the createOffer api has switched to be based on Promise instead of successCallback and failCallback as params, so this may not work on newer versions, see: developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/…
    – Dickeylth
    Commented Sep 26, 2019 at 12:11
  • Doesn't seem to function for me. None of the address kinds are returned. Error with Permissions-Policy header: Unrecognized feature: 'interest-cohort'. (index):63 Uncaught TypeError: Cannot read properties of null (reading '1') at handleCandidate ((index):63:59) at RTCPeerConnection.pc.onicecandidate ((index):77:25) (index):63 Uncaught TypeError: Cannot read properties of null (reading '1') at handleCandidate ((index):63:59) at (index):98:29 at Array.forEach (<anonymous>) at (index):96:27
    – zmechanic
    Commented Feb 5, 2022 at 20:02
  • yes i have same error as zmechanic, any solution ?? Commented Dec 7, 2023 at 3:06
14

The WebRTC API can be used to retrieve the client's local IP.

However the browser may not support it, or the client may have disabled it for security reasons. In any case, one should not rely on this "hack" on the long term as it is likely to be patched in the future (see Cullen Fluffy Jennings's answer).

The ECMAScript 6 code below demonstrates how to do that.

/* ES6 */
const findLocalIp = (logInfo = true) => new Promise( (resolve, reject) => {
    window.RTCPeerConnection = window.RTCPeerConnection 
                            || window.mozRTCPeerConnection 
                            || window.webkitRTCPeerConnection;

    if ( typeof window.RTCPeerConnection == 'undefined' )
        return reject('WebRTC not supported by browser');

    let pc = new RTCPeerConnection();
    let ips = [];

    pc.createDataChannel("");
    pc.createOffer()
     .then(offer => pc.setLocalDescription(offer))
     .catch(err => reject(err));
    pc.onicecandidate = event => {
        if ( !event || !event.candidate ) {
            // All ICE candidates have been sent.
            if ( ips.length == 0 )
                return reject('WebRTC disabled or restricted by browser');

            return resolve(ips);
        }

        let parts = event.candidate.candidate.split(' ');
        let [base,componentId,protocol,priority,ip,port,,type,...attr] = parts;
        let component = ['rtp', 'rtpc'];

        if ( ! ips.some(e => e == ip) )
            ips.push(ip);

        if ( ! logInfo )
            return;

        console.log(" candidate: " + base.split(':')[1]);
        console.log(" component: " + component[componentId - 1]);
        console.log("  protocol: " + protocol);
        console.log("  priority: " + priority);
        console.log("        ip: " + ip);
        console.log("      port: " + port);
        console.log("      type: " + type);

        if ( attr.length ) {
            console.log("attributes: ");
            for(let i = 0; i < attr.length; i += 2)
                console.log("> " + attr[i] + ": " + attr[i+1]);
        }

        console.log();
    };
} );

Notice I write return resolve(..) or return reject(..) as a shortcut. Both of those functions do not return anything.

Then you may have something this :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Local IP</title>
</head>
<body>
    <h1>My local IP is</h1>
    <p id="ip">Loading..</p>
    <script src="ip.js"></script>
    <script>
    let p = document.getElementById('ip');
    findLocalIp().then(
        ips => {
            let s = '';
            ips.forEach( ip => s += ip + '<br>' );
            p.innerHTML = s;
        },
        err => p.innerHTML = err
    );
    </script>
</body>
</html>
2
  • 2
    Gives me IP address like 11596857-1ab3-477c-b21d-ad6de5311658.local Commented Feb 4, 2022 at 8:27
  • 1
    @run_the_race Check out the comment by injaon on afourney answer
    – ytan11
    Commented Dec 1, 2022 at 1:25
9

I cleaned up mido's post and then cleaned up the function that they found. This will either return false or an array. When testing remember that you need to collapse the array in the web developer console otherwise it's nonintuitive default behavior may deceive you in to thinking that it is returning an empty array.

function ip_local()
{
 var ip = false;
 window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || false;

 if (window.RTCPeerConnection)
 {
  ip = [];
  var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};
  pc.createDataChannel('');
  pc.createOffer(pc.setLocalDescription.bind(pc), noop);

  pc.onicecandidate = function(event)
  {
   if (event && event.candidate && event.candidate.candidate)
   {
    var s = event.candidate.candidate.split('\n');
    ip.push(s[0].split(' ')[4]);
   }
  }
 }

 return ip;
}

Additionally please keep in mind folks that this isn't something old-new like CSS border-radius though one of those bits that is outright not supported by IE11 and older. Always use object detection, test in reasonably older browsers (e.g. Firefox 4, IE9, Opera 12.1) and make sure your newer scripts aren't breaking your newer bits of code. Additionally always detect standards compliant code first so if there is something with say a CSS prefix detect the standard non-prefixed code first and then fall back as in the long term support will eventually be standardized for the rest of it's existence.

5
  • you're redeclaring ip - line 3 and line 8. Commented Apr 13, 2018 at 14:23
  • @Anu WebRTC was not introduced until Internet Explorer 15 (or "Edge 15") so no. That is why on the fourth line above if none of the objects exists the function will return false. If there is another way of achieving this in IE then I'm not aware of it at this time.
    – John
    Commented Jun 3, 2018 at 13:06
  • @John - how do we pass the return value to a php variable ? Via a hidden post ?
    – MarcoZen
    Commented Jun 9, 2018 at 8:10
  • @MarcoZen You can either use <input name="example1" type="hidden" value="whatever" /> or use an AJAX POST in such a situation. I highly recommend studying my ajax() function here: jabcreations.com/docs/javascript
    – John
    Commented Jun 9, 2018 at 12:17
  • Just found out that some browsers (e.g. Chrome) now block providing the IP - same code now resolves to an mDNS hostname, if no Video/Audio permission is requested. See groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU Commented Dec 22, 2019 at 4:52
8

Chrome 76+

Last year I used Linblow's answer (2018-Oct-19) to successfully discover my local IP via javascript. However, recent Chrome updates (76?) have wonked this method so that it now returns an obfuscated IP, such as: 1f4712db-ea17-4bcf-a596-105139dfd8bf.local

If you have full control over your browser, you can undo this behavior by turning it off in Chrome Flags, by typing this into your address bar:

chrome://flags

and DISABLING the flag Anonymize local IPs exposed by WebRTC

In my case, I require the IP for a TamperMonkey script to determine my present location and do different things based on my location. I also have full control over my own browser settings (no Corporate Policies, etc). So for me, changing the chrome://flags setting does the trick.

Sources:

https://groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU

https://codelabs.developers.google.com/codelabs/webrtc-web/index.html

3
  • that flag might go away. It currently seems like extensions still get IPs so you might try to get it from the background script. Long-term all bets are off though. Commented Nov 20, 2019 at 22:05
  • 2
    According to groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU, the IP should still be returned if you implement your solution that it requests Audio/Video permission. Commented Dec 22, 2019 at 4:53
  • I was looking for this for the exact same reason, and lmao when I read the last paragraph of your post. Can't believe this is still possible in late 2020, but so glad that it is! +1 for sharing.
    – Kenny83
    Commented Sep 13, 2020 at 19:51
6

function getUserIP(onNewIP) { //  onNewIp - your listener function for new IPs
  //compatibility for firefox and chrome
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
  var pc = new myPeerConnection({
      iceServers: []
    }),
    noop = function() {},
    localIPs = {},
    ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
    key;

  function iterateIP(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  onNewIP
  //create a bogus data channel
  pc.createDataChannel("");

  // create offer and set local description
  pc.createOffer().then(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(iterateIP);
    });

    pc.setLocalDescription(sdp, noop, noop);
  }).catch(function(reason) {
    // An error occurred, so handle the failure to connect
  });

  //listen for candidate events
  pc.onicecandidate = function(ice) {
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(iterateIP);
  };
}
getUserIP(console.log)

5
  • Please use the editor options to format your code appropriately.
    – 31piy
    Commented Mar 14, 2017 at 17:33
  • 3
    It would be great if you'd not only just drop some code, but also give an explanation of what is going on in his and your code. It helps the question author and other users. It's good if it works, but knowing why is even more important in my opinion.
    – davejal
    Commented Mar 14, 2017 at 18:04
  • any IE compatible solutions?
    – Anu
    Commented Jun 1, 2018 at 7:40
  • 1
    The comment is a copy paste of this article: ourcodeworld.com/articles/read/257/…
    – Darkshifty
    Commented Aug 29, 2019 at 12:56
  • Just found out that some browsers (e.g. Chrome) now block providing the IP - same code now resolves to an mDNS hostname, if no Video/Audio permission is requested. See groups.google.com/forum/#!topic/discuss-webrtc/6stQXi72BEU Commented Dec 22, 2019 at 4:52
5

Now supported in internal-ip!

An RTCPeerConnection can be used. In browsers like Chrome where a getUserMedia permission is required, we can just detect available input devices and request for them.

const internalIp = async () => {
    if (!RTCPeerConnection) {
        throw new Error("Not supported.")
    }

    const peerConnection = new RTCPeerConnection({ iceServers: [] })

    peerConnection.createDataChannel('')
    peerConnection.createOffer(peerConnection.setLocalDescription.bind(peerConnection), () => { })

    peerConnection.addEventListener("icecandidateerror", (event) => {
        throw new Error(event.errorText)
    })

    return new Promise(async resolve => {
        peerConnection.addEventListener("icecandidate", async ({candidate}) => {
            peerConnection.close()
            
            if (candidate && candidate.candidate) {
                const result = candidate.candidate.split(" ")[4]
                if (result.endsWith(".local")) {
                    const inputDevices = await navigator.mediaDevices.enumerateDevices()
                    const inputDeviceTypes = inputDevices.map(({ kind }) => kind)

                    const constraints = {}

                    if (inputDeviceTypes.includes("audioinput")) {
                        constraints.audio = true
                    } else if (inputDeviceTypes.includes("videoinput")) {
                        constraints.video = true
                    } else {
                        throw new Error("An audio or video input device is required!")
                    }

                    const mediaStream = await navigator.mediaDevices.getUserMedia(constraints)
                    mediaStream.getTracks().forEach(track => track.stop())
                    resolve(internalIp())
                }
                resolve(result)
            }
        })
    })
}
2
  • Thank you! That's exactly what I needed. Just Javascript, no HTML and understandable code :)
    – JackieNBee
    Commented Oct 6, 2020 at 14:47
  • 1
    Hahaha so I had to allow access to my microphone for this but it ended up spitting out the IP address for hyperv vswitch
    – Dmitri DB
    Commented Feb 18, 2021 at 21:42
4

You can find more info about what limitations browsers will likely add to mitigate this and what IETF is doing about it as well as why this is needed at IETF SPEC on IP handling

-4

Try to use the OS package to find the wifi ip.

const os = require('os');
console.log('IP Address: ' + JSON.stringify(os.networkInterfaces()['Wi-Fi']).match(/"192.168.\d+.\d+"/g)[0])

Not the answer you're looking for? Browse other questions tagged or ask your own question.