Client IP in Express behind Nginx
Last week I worked on a web application that needed to restrict some pages and routes to specific IP addresses. This is considered security through obscurity but still works for simpler non-critical cases.
The application is reverse proxied through Nginx and does not see client's real IP addresses directly (it sees the proxy's). The actual
client IP address is passed through the header X-Real-IP
by using the proxy_set_header
directive in the reverse proxy
configuration:
location / {
proxy_pass http://127.0.0.1:9091;
proxy_set_header X-Real-IP $remote_addr;
# ... rest of configuration
}
The app runs without a proxy in some configurations (development, test) and that requires IP check inside the app itself. Otherwise the check could have been implemented on Nginx as well.
At the beginning I started to look at the existing Node.js packages to obtain the client's IP. One of such packages was ipware. It seems to be a port from some other platform's similar library. It did not work for me. There seems to be some confusion over HTTP header normalization in Node.
Other such libraries seem to be quite complex as well and contain too much magic. I ended up using my own middleware that sets
req.clientIP
from the header when it's given. The IP address is taken from the client socket when the header does not exist. This
is done with a middleware:
module.exports = function () {
return function (req, res, next) {
var realIP = req.get("x-real-ip");
if (realIP) {
req.clientIP = realIP;
} else {
req.clientIP = req.socket.remoteAddress;
}
next();
};
};
The HTTP header normalization in Node is documented in the API reference:
Header names are lower-cased.
There is no transform that replaces dashes with underscores or does some other fancy stuff.
Security warning
In the current application I also added proper password-based authentication and authorization. The approach with IP aadress inside an header has a
potential security hole: when the app is not behind Nginx then anyone can set their "IP address" by setting the
X-Real-IP
request header!