Express.js: how to get remote client address
Asked Answered
S

16

416

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?

Simplistic answered 1/6, 2012 at 11:49 Comment(4)
How about using node-ipware as per the explanation here.Ploughshare
if you can not get req.hostname like 'example.com': https://mcmap.net/q/63576/-how-to-get-the-full-url-in-expressClimb
Possible duplicate of How to determine a user's IP address in nodeSymer
NB I decided to check out node-ipware mentioned above (a long while ago) and this is now deprecated. The suggested replacement at @fullerstack/nax-ipware has a license that says source code is "proprietary and confidential".Pewee
W
716

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.socket.remoteAddress 

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

Wexler answered 1/6, 2012 at 11:53 Comment(13)
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!Nadia
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.Faythe
If you plan to use the IP in browser then http(s)://[ipv6]:port Notice the [ ] are needed, hope this helpsAtp
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.Leandra
why does this print ::1Sandusky
@Sandusky ::1 is the IPv6 address for localhostLatent
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.Sirius
What would happen if client would set the x-forwarded-for header itself?Gertrudis
@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 guardEncircle
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()Audiogenic
i can't get real visitor ip but i get localhost ipSech
It should be noted that this header is also included in AWS Elastic Load Balancer requests.Erikaerikson
I want to get real visitors ip but this is giving me the local ipIntrogression
G
324

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.
Gelb answered 31/1, 2013 at 17:31 Comment(11)
My answer was more general (not tied to Express), but if you're using Express that's indeed the better way.Wexler
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 fileLecia
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]Gamete
Is this solution safe?Pozzy
@Pozzy 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.Gelb
@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?Alcyone
@Alcyone 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.Gamete
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.Holoblastic
Why does this give me ::1?Sandusky
@Sandusky I have the same issue as you. Getting ::1 on localhost with every suggestedGeddes
WARNING: You must use app.set('trust proxy', '<IP of your proxy (usually 'loopback')>') instead of app.set('trust proxy', true)! The latter may result in client being able to set req.ip to whatever value it wants!Dinorahdinosaur
V
75

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

Violaviolable answered 28/8, 2013 at 7:27 Comment(4)
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.Joyless
@Violaviolable '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.Plexus
For me, IP address is available under req.headers['x-real-ip'] even in nginx.conf header is set with capitalised letters.Malaguena
This is what solved it for me. Even with trust-proxy set to true, it was still using the local 127.0.0.1 addressBolero
F
46

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 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. appengine-user-ip (Google App Engine)
  10. req.connection.remoteAddress
  11. req.socket.remoteAddress
  12. req.connection.socket.remoteAddress
  13. req.info.remoteAddress
  14. Cf-Pseudo-IPv4 (Cloudflare fallback)
  15. request.raw (Fastify)

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

Disclose: I am not associated with the library.

Ferryboat answered 3/4, 2020 at 2:45 Comment(1)
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.Fabrianne
C
34

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.

Cryptomeria answered 6/2, 2014 at 16:46 Comment(1)
req.connection is marked as deprecated nowAnibalanica
P
24
  1. Add app.set('trust proxy', true)
  2. Use req.ip or req.ips in the usual way
Permian answered 31/7, 2017 at 12:18 Comment(4)
This is the way to go. See expressjs.com/en/guide/behind-proxies.html for details.Duer
So easy and concise. Love a simple answer.Woodruff
Just sharing that this approach is also referred to in the docs for the express-rate-limit node package 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.Huan
Note to self: According to this answer, Cloudflare sets the CF-Connecting-IP and the X-Forwarded-For headers on every request.Smaragdine
N
11

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;

Naji answered 9/5, 2019 at 22:42 Comment(1)
req.headers['x-real-ip'] returning undefined and req.connection.remoteAddress returning ::ffff:127.0.0.1.Junno
S
9

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

let ip = req.connection.remoteAddress.split(`:`).pop();
Spue answered 25/1, 2019 at 19:59 Comment(0)
M
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);
});
Mallee answered 21/11, 2018 at 11:0 Comment(2)
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?, some tips and advice about self-promotion, What is the exact definition of "spam" for Stack Overflow?, and What makes something spam.Keilakeily
This can be handled by setting trust proxy to true and using req.ip. Why make a module for it?Mindimindless
S
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.

Siracusa answered 3/1, 2017 at 21:51 Comment(0)
T
4

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.

Tryma answered 31/1, 2020 at 20:9 Comment(0)
P
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.

Petcock answered 9/4, 2017 at 3:28 Comment(0)
C
2

var ip = req.connection.remoteAddress;

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

Couchman answered 1/2, 2018 at 14:6 Comment(2)
ouput is like:- ::ffff:XXX.XX.XX.XX from this we will get ipCouchman
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.Weeper
G
1

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

var ip = req.headers['x-forwarded-for'].split(',')[0];
Glamorize answered 3/5, 2018 at 20:20 Comment(0)
F
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;
    }
Florio answered 21/8, 2019 at 15:6 Comment(0)
R
-1

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);
Recriminate answered 20/11, 2019 at 18:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.