133

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) Commented Nov 25, 2013 at 13:55
  • My IP address is captured thru HTTP_X_FORWARDED_FOR on that page, just sayin`. Commented Nov 25, 2013 at 14:12
  • 83
    Why ask then? Because maybe, just maybe, I don't know everything. Commented Nov 26, 2013 at 0:18
  • 1
    These guys do it: whatismyproxy.com Commented Sep 29, 2019 at 11:32
  • 1
    @likebike Nice one. Looking into how they are doing this. Commented Nov 16, 2019 at 0:42

9 Answers 9

135

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).

Sign up to request clarification or add additional context in comments.

10 Comments

It is just working on the chrome and firefox, And NOT on the IE, Edge or safari
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.
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
looking at cleancss.com and having that flag on "Default", it still works. On other test sites, hiding works. Strange.
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
|
97

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 Comments

@dampee - I believe Edge does not support data channels at the moment.
What is a bogus data channel? Can't find any reference on google
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/…
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
yes i have same error as zmechanic, any solution ??
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 Comments

Gives me IP address like 11596857-1ab3-477c-b21d-ad6de5311658.local
@run_the_race Check out the comment by injaon on afourney answer
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 Comments

you're redeclaring ip - line 3 and line 8.
@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 - how do we pass the return value to a php variable ? Via a hidden post ?
@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
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
9

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 Comments

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.
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.
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.
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 Comments

Please use the editor options to format your code appropriately.
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.
any IE compatible solutions?
The comment is a copy paste of this article: ourcodeworld.com/articles/read/257/…
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
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 Comments

Thank you! That's exactly what I needed. Just Javascript, no HTML and understandable code :)
Hahaha so I had to allow access to my microphone for this but it ended up spitting out the IP address for hyperv vswitch
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

Comments

-5

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])

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.