307

I don't completely understand how I should get a remote user IP address.

Let's say I have a simple request route such as:

app.get(/, function (req, res){
   var forwardedIpsStr = req.header('x-forwarded-for');
   var IP = '';

   if (forwardedIpsStr) {
      IP = forwardedIps = forwardedIpsStr.split(',')[0];  
   }
});

Is the above approach correct to get the real user IP address or is there a better way? And what about proxies?

Elrond_EGLDer
  • 47,430
  • 25
  • 189
  • 180
Erik
  • 11,695
  • 42
  • 119
  • 194
  • 1
    How about using **[node-ipware](https://github.com/un33k/node-ipware)** as per the explanation **[here](http://stackoverflow.com/a/26310355/458879)**. – un33k Nov 03 '14 at 02:30
  • if you can not get req.hostname like 'example.com': http://stackoverflow.com/a/37824880/5333284 – zhi.yang Jun 15 '16 at 02:03
  • 1
    Possible duplicate of [How to determine a user's IP address in node](http://stackoverflow.com/questions/8107856/how-to-determine-a-users-ip-address-in-node) – Dan Dascalescu May 07 '17 at 07:58

16 Answers16

562

If you are running behind a proxy like NGiNX or what have you, only then you should check for 'x-forwarded-for':

var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

If the proxy isn't 'yours', I wouldn't trust the 'x-forwarded-for' header, because it can be spoofed.

Cmte Cardeal
  • 159
  • 2
  • 8
alessioalex
  • 57,958
  • 15
  • 152
  • 119
  • 2
    This is correct, however in my situation I had to use square brackets (see above edit) Also make sure you have x-forwarded-for enabled in your nginx configuration. Works like a charm! – ninehundreds Jul 04 '13 at 15:23
  • 49
    You need to keep in mind that you have to put this directive `proxy_set_header X-Forwarded-For $remote_addr;` into your nginx configuration in case you are using your own reverse proxy. – Coxer May 04 '15 at 12:14
  • If you plan to use the IP in browser then http(s)://[ipv6]:port Notice the [ ] are needed, hope this helps – psuhas May 18 '16 at 21:03
  • 66
    copy-pasta users please note: This can return a comma separated list of IP addresses. We had a bug from a dev copying this and comparing the result to an IP. Perhaps do something like `var ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || '').split(',')[0].trim();` to get client IP. – Davy Jones Feb 21 '18 at 12:47
  • 1
    why does this print `::1` – Rishav Jun 07 '19 at 20:36
  • 5
    @Rishav ::1 is the IPv6 address for localhost – Daniel O Jul 05 '19 at 15:15
  • Please refer to @Haozhun answer. Express takes care of remote IP addresses using 'trust proxy'. X-Forwarded-For can contain extra information such as Google cloud has a chain of different IPs. – Luke Mar 04 '20 at 00:02
  • What would happen if client would set the `x-forwarded-for` header itself? – Slava Fomin II Apr 11 '20 at 13:26
  • 2
    @DavyJones 's snippet is not working with typescript since any unlisted req.headers (including "X-" headers) is typed `string|string[]`, and can't have `split` method. can be rewritten and should be rewritten with type guard – Александр Усков Jul 16 '20 at 13:07
  • This will get 127.0.0.1 from this text ::ffff:127.0.0.1 ```const ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress).split(':').pop()``` – coderbuzz Aug 26 '20 at 00:36
  • i can't get real visitor ip but i get localhost ip – naufal syarifuddin Dec 02 '20 at 02:43
268

While the answer from @alessioalex works, there's another way as stated in the Express behind proxies section of Express - guide.

  1. Add app.set('trust proxy', true) to your express initialization code.
  2. When you want to get the ip of the remote client, use req.ip or req.ips in the usual way (as if there isn't a reverse proxy)

Optional reading:

  • Use req.ip or req.ips. req.connection.remoteAddress does't work with this solution.
  • More options for 'trust proxy' are available if you need something more sophisticated than trusting everything passed through in x-forwarded-for header (for example, when your proxy doesn't remove preexisting x-forwarded-for header from untrusted sources). See the linked guide for more details.
  • If your proxy server does not populated x-forwarded-for header, there are two possibilities.
    1. The proxy server does not relay the information on where the request was originally. In this case, there would be no way to find out where the request was originally from. You need to modify configuration of the proxy server first.
      • For example, if you use nginx as your reverse proxy, you may need to add proxy_set_header X-Forwarded-For $remote_addr; to your configuration.
    2. The proxy server relays the information on where the request was originally from in a proprietary fashion (for example, custom http header). In such case, this answer would not work. There may be a custom way to get that information out, but you need to first understand the mechanism.
Haozhun
  • 5,663
  • 3
  • 26
  • 47
  • 4
    My answer was more general (not tied to Express), but if you're using Express that's indeed the better way. – alessioalex May 30 '13 at 08:58
  • 4
    Your proxy server has to have the header 'x-forwarded-for' set to the remote address. In the case of nginx for example, you should have proxy_set_header X-Forwarded-For $remote_addr in your config file – Kamagatos Aug 08 '14 at 03:29
  • 2
    Behind IIS with IISnode as proxy, `app.enable('trust proxy')` works too to use `req.ip`. Except I get the port with it `1.2.3.4:56789`. To strip that, I do `var ip = req.ip.split(':')[0]` – Christiaan Westerbeek Dec 04 '14 at 08:48
  • Is this solution safe? – Daniel Kmak Apr 07 '15 at 21:59
  • @Daniel What do you mean by safe? This is documented behavior. Therefore it can't change. In terms of security, it's no less secure than the other solution using `req.headers['x-forwarded-for']`. I believe both looks at the `x-forwarded-for` http header. – Haozhun Apr 08 '15 at 04:39
  • @Christiaan Westerbeek, that solution is not entirely right, sometimes you will get something like ::ffff:1.2.3.4, so, if that'ss the case, I was wondering are there other cases to contemplate to make a "cleaner" method? and what would be the best way to do so? – jmdiego May 05 '15 at 17:49
  • @jmdiego You can ask a new question on SO. I'll try and answer it there. This trail of comments may become to long to be helpful for others. – Christiaan Westerbeek May 05 '15 at 18:31
  • 4
    I feel that this answer lacks security and needs updating. You should ALWAYS define which proxies your application trusts. The accepted answer at least has a little notice about spoofing. That said, it IS a better solution to use a library like this if you're using express, but the quoted code is incorrect and is not found on the linked resource. – Phil Sep 21 '17 at 08:26
  • 2
    Why does this give me `::1`? – Rishav Jun 07 '19 at 20:36
  • @Rishav I have the same issue as you. Getting ::1 on localhost with every suggested – wongz May 20 '21 at 03:20
60

In nginx.conf file:
proxy_set_header X-Real-IP $remote_addr;

In node.js server file:
var ip = req.headers['x-real-ip'] || req.connection.remoteAddress;

note that express lowercases headers

electblake
  • 1,707
  • 16
  • 25
ququzone
  • 625
  • 5
  • 2
  • 3
    Welcome to Stack Overflow! Rather than only post a block of code, please explain why this code solves the problem posed. Without an explanation, this is not an answer. – Artemix Aug 28 '13 at 07:50
  • 1
    @ququzone 's answer is ok. The explanation is set a custom header on the request named "x-real-ip" which takes the original ip address from the visitor. It works for me with node and socket.io. – coffekid Nov 16 '13 at 22:13
  • 9
    For me, IP address is available under `req.headers['x-real-ip']` even in `nginx.conf` header is set with capitalised letters. – Nik Sumeiko Dec 05 '14 at 07:46
  • This is what solved it for me. Even with trust-proxy set to true, it was still using the local 127.0.0.1 address – David Jul 06 '16 at 04:47
32

Particularly for node, the documentation for the http server component, under event connection says:

[Triggered] when a new TCP stream is established. [The] socket is an object of type net.Socket. Usually users will not want to access this event. In particular, the socket will not emit readable events because of how the protocol parser attaches to the socket. The socket can also be accessed at request.connection.

So, that means request.connection is a socket and according to the documentation there is indeed a socket.remoteAddress attribute which according to the documentation is:

The string representation of the remote IP address. For example, '74.125.127.100' or '2001:4860:a005::68'.

Under express, the request object is also an instance of the Node http request object, so this approach should still work.

However, under Express.js the request already has two attributes: req.ip and req.ips

req.ip

Return the remote address, or when "trust proxy" is enabled - the upstream address.

req.ips

When "trust proxy" is true, parse the "X-Forwarded-For" ip address list and return an array, otherwise an empty array is returned. For example if the value were "client, proxy1, proxy2" you would receive the array ["client", "proxy1", "proxy2"] where "proxy2" is the furthest down-stream.

It may be worth mentioning that, according to my understanding, the Express req.ip is a better approach than req.connection.remoteAddress, since req.ip contains the actual client ip (provided that trusted proxy is enabled in express), whereas the other may contain the proxy's IP address (if there is one).

That is the reason why the currently accepted answer suggests:

var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

The req.headers['x-forwarded-for'] will be the equivalent of express req.ip.

Pavel P
  • 13,962
  • 11
  • 68
  • 109
Edwin Dalorzo
  • 70,022
  • 25
  • 131
  • 191
17
  1. Add app.set('trust proxy', true)
  2. Use req.ip or req.ips in the usual way
Bald
  • 1,888
  • 3
  • 23
  • 31
  • 2
    This is the way to go. See https://expressjs.com/en/guide/behind-proxies.html for details. – O. Jones Sep 29 '17 at 20:48
  • So easy and concise. Love a simple answer. – gilbert-v Jun 13 '19 at 15:21
  • Just sharing that this approach is also referred to in the docs for the [express-rate-limit node package](https://www.npmjs.com/package/express-rate-limit#usage) and by setting `app.set('trust proxy', true);` and referencing `req.ip` i was able to get all the desired and expected behaviour for my site which uses cloudflare as a proxy. – user1063287 Sep 18 '20 at 10:19
17

If you are fine using 3rd-party library. You can check request-ip.

You can use it is by

import requestIp from 'request-ip';

app.use(requestIp.mw())

app.use((req, res) => {
  const ip = req.clientIp;
});

The source code is quite long, so I won't copy here, you can check at https://github.com/pbojinov/request-ip/blob/master/src/index.js

Basically,

It looks for specific headers in the request and falls back to some defaults if they do not exist.

The user ip is determined by the following order:

  1. X-Client-IP
  2. X-Forwarded-For (Header may return multiple IP addresses in the format: "client IP, proxy 1 IP, proxy 2 IP", so we take the the first one.)
  3. CF-Connecting-IP (Cloudflare)
  4. Fastly-Client-Ip (Fastly CDN and Firebase hosting header when forwared to a cloud function)
  5. True-Client-Ip (Akamai and Cloudflare)
  6. X-Real-IP (Nginx proxy/FastCGI)
  7. X-Cluster-Client-IP (Rackspace LB, Riverbed Stingray)
  8. X-Forwarded, Forwarded-For and Forwarded (Variations of #2)
  9. req.connection.remoteAddress
  10. req.socket.remoteAddress
  11. req.connection.socket.remoteAddress
  12. req.info.remoteAddress

If an IP address cannot be found, it will return null.

Disclose: I am not associated with the library.

Hongbo Miao
  • 31,551
  • 46
  • 124
  • 206
  • Very nice @hongbo! Worth mentioning that since node is case sensitive that one should use `req.headers['x-client-ip']`, `req.headers['x-forwarded-for']`, etc if one would like to skip the middleware solution. – Matthew May 21 '21 at 14:54
8

This is just additional information for this answer.

If you are using nginx, you would add proxy_set_header X-Real-IP $remote_addr; to the location block for the site. /etc/nginx/sites-available/www.example.com for example. Here is a example server block.

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    location / {
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_pass http://127.0.1.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

After restarting nginx, you will be able to access the ip in your node/express application routes with req.headers['x-real-ip'] || req.connection.remoteAddress;

learnsomemore
  • 516
  • 4
  • 12
6

I wrote a package for that purpose. You can use it as express middleware. My package is published here: https://www.npmjs.com/package/express-ip

You can install the module using

npm i express-ip

Usage

const express = require('express');
const app = express();
const expressip = require('express-ip');
app.use(expressip().getIpInfoMiddleware);

app.get('/', function (req, res) {
    console.log(req.ipInfo);
});
rene
  • 37,946
  • 78
  • 99
  • 132
Oyetoke Tobi
  • 129
  • 2
  • 8
  • 5
    You *must disclose affiliation in the post* when linking to something you're affiliated with. If you don't disclose affiliation, it's considered spam. Disclosure must be explicit, but doesn't need to be formal (e.g. For your own *personal* content: "on my site…", "on my blog…", etc.). See: [**What signifies "Good" self promotion?**](//meta.stackexchange.com/q/182212), [some tips and advice about self-promotion](/help/promotion), [What is the exact definition of "spam" for Stack Overflow?](//meta.stackoverflow.com/q/260638), and [What makes something spam](//meta.stackexchange.com/a/58035). – Makyen Mar 30 '20 at 17:37
5

I know this question has been answered, but here's how I got mine to work.

let ip = req.connection.remoteAddress.split(`:`).pop();
Lorem Ipsum
  • 51
  • 1
  • 4
4

According to Express behind proxies, req.ip has taken into account reverse proxy if you have configured trust proxy properly. Therefore it's better than req.connection.remoteAddress which is obtained from network layer and unaware of proxy.

abbr
  • 4,671
  • 5
  • 22
  • 38
2

This worked for me better than the rest. My sites are behind CloudFlare and it seemed to require cf-connecting-ip.

req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress

Didn't test Express behind proxies as it didn't say anything about this cf-connecting-ip header.

ile
  • 1,693
  • 1
  • 14
  • 16
2

In my case, similar to this solution, I ended up using the following x-forwarded-for approach:

let ip = (req.headers['x-forwarded-for'] || '').split(',')[0];

x-forwarded-for header will keep on adding the route of the IP from the origin all the way to the final destination server, thus if you need to retrieve the origin client's IP, this would be the first item of the array.

Menelaos Kotsollaris
  • 4,517
  • 6
  • 50
  • 61
1

var ip = req.connection.remoteAddress;

ip = ip.split(':')[3];

Bhulawat Ajay
  • 227
  • 2
  • 3
  • 12
  • ouput is like:- ::ffff:XXX.XX.XX.XX from this we will get ip – Bhulawat Ajay Feb 01 '18 at 14:07
  • 3
    I think `ip = ip.split(':').pop();` will be batter in this case if normal ip i.e 127.0.0.1 will come It will be still able to give you ip. – 9me May 12 '18 at 07:08
1

With could-flare, nginx and x-real-ip support

var user_ip;

    if(req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
      let first = req.headers['cf-connecting-ip'].split(', ');
      user_ip = first[0];
    } else {
      let user_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
    }
kakopappa
  • 4,849
  • 4
  • 49
  • 72
0

The headers object has everything you need, just do this:

var ip = req.headers['x-forwarded-for'].split(',')[0];
Zoe
  • 23,712
  • 16
  • 99
  • 132
Juan David Arce
  • 319
  • 2
  • 10
0

Putting all together witk @kakopappa solution plus morgan logging of the client ip address:

morgan.token('client_ip', function getId(req) {
    return req.client_ip
});
const LOG_OUT = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :client_ip'
self.app.use(morgan(LOG_OUT, {
    skip: function(req, res) { // custom logging: filter status codes
        return res.statusCode < self._options.logging.statusCode;
    }
}));

// could-flare, nginx and x-real-ip support
var getIpInfoMiddleware = function(req, res, next) {
    var client_ip;
    if (req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
        var first = req.headers['cf-connecting-ip'].split(', ');
        client_ip = first[0];
    } else {
        client_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
    }
    req.client_ip = client_ip;
    next();
};
self.app.use(getIpInfoMiddleware);
loretoparisi
  • 12,864
  • 9
  • 78
  • 108