3 min read
nodejs expressjs heroku networking webdev

Why Your App is Logging the Wrong IP Address (And How to Fix It)

A quick anecdote about how I was incorrectly handling IP logging in an old Node.js/Express project deployed on Heroku, and the simple fix.

Hey guys, it’s me again — back with another story about IP addresses and networking stuff. This time I want to share a quick anecdote about how I was incorrectly handling IP logging in one of my old Node.js/Express side projects that I deployed on Heroku (back when they still had a free tier… RIP 🥀).

So the project had a simple middleware that looked like this:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  console.log('Client IP:', req.socket.remoteAddress);
  next();
});

Pretty straightforward, right?

Well… not quite.

When I checked the logs, instead of seeing real client IP addresses, all I saw were internal Heroku server IPs. At the time, I had absolutely no idea why.


The “aha!” moment: Heroku uses reverse proxies

What I didn’t know back then is that Heroku automatically places your app behind load balancers and reverse proxies.

Unlike something like a raw AWS EC2 instance — where traffic hits your machine directly — Heroku as most of modern cloud hosting providers intercepts all inbound traffic first.

Here’s roughly what I thought was happening:

User → Your Express App

But here’s what actually happens:

User
  |
  v
[ Heroku Load Balancer / Reverse Proxy ]
  |
  v
Your Express App

So when Express reads:

req.socket.remoteAddress

…it’s not showing the user’s IP. It’s showing Heroku’s proxy IP, because that’s what is technically connected to your app.


How reverse proxies preserve the real client IP

Reverse proxies work like middlemen. They accept the incoming request, open a separate connection to your server, and then forward everything along.

To make sure the origin server (your app) still knows the real user IP, proxies add a special header:

X-Forwarded-For: <real client IP>

So the request flow looks like this:

User IP 203.0.113.42
     |
     | (User connects)
     v
Heroku Proxy
     |
     |  Adds header: X-Forwarded-For: 203.0.113.42
     v
Your Express App

Once I understood that, the fix became easy.


Two ways to fix it in Express

Option 1: Tell Express to trust the proxy

When you enable this, Express automatically uses X-Forwarded-For for req.ip.

const express = require('express');
const app = express();

app.set('trust proxy', true);

app.use((req, res, next) => {
  console.log('Client IP:', req.ip);
  next();
});

This is the recommended approach for Heroku, Render, Vercel, etc.


Option 2: Read the header manually

If you prefer to handle it yourself:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  console.log('Client IP:', req.headers['x-forwarded-for']);
  next();
});

Just note that the header can contain a comma-separated list if multiple proxies are in the chain. Usually, the first entry is the real client.


The takeaway

If your Node/Express app runs behind any cloud hosting, CDN, load balancer, or reverse proxy (Heroku, Vercel, Netlify, AWS ELB, Cloudflare, Nginx, etc.), remember:

What you’re usingWhat you get
req.socket.remoteAddress❌ Proxy’s IP (useless)
req.ip with trust proxy✅ Real client IP
req.headers['x-forwarded-for']✅ Real client IP (manual)

Bottom line: Always check how your hosting platform handles incoming traffic. Most modern providers use reverse proxies, which means you’ll need to configure your app to read the X-Forwarded-For header — either automatically via trust proxy or manually.

This is one of those things you learn after staring at logs wondering why every single user of your app seems to live inside your hosting provider’s data center. 😅


This is a follow-up to my previous article: I’ve Been a Developer for over 8 Years, and I Just Actually Understood IP Addresses

Comments