devops2025-04-05·7·127/348

Express.js trust proxy 설정 (리버스 프록시)

Configuring Express.js trust proxy setting to correctly handle requests behind load balancers, CDNs, and reverse proxies.

Express.js trust proxy 설정 (리버스 프록시)

Introduction

When your Express.js application runs behind a reverse proxy like Nginx, Apache, or a cloud load balancer, you need to configure trust proxy to correctly identify client IP addresses, protocol, and host headers. Without this setting, your application sees the proxy's IP instead of the client's.

Environment

node --version
# v20.11.0

npm list express
# my-app@1.0.0
# +-- express@4.18.2

# Running behind Nginx reverse proxy
nginx -v
# nginx/1.24.0

Problem

The application was logging the proxy's IP instead of the client's:

app.get('/api/info', (req, res) => {
  res.json({
    ip: req.ip,          // Returns proxy IP: 127.0.0.1
    protocol: req.protocol, // Returns 'http' even though HTTPS
    hostname: req.hostname // Returns internal hostname
  });
});
{
  "ip": "127.0.0.1",
  "protocol": "http",
  "hostname": "internal-server"
}

Analysis

Without trust proxy, Express uses the direct connection's information:

Client (203.0.113.50) → Nginx (127.0.0.1) → Express
Express sees: 127.0.0.1 (Nginx's IP)
Expected: 203.0.113.50 (Client's IP)

The trust proxy setting tells Express to trust the X-Forwarded-* headers set by the proxy.

Solution

Solution 1: Enable trust proxy

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

// Trust first proxy
app.set('trust proxy', true);

// Or trust specific proxy
app.set('trust proxy', '127.0.0.1');

// Or trust subnet
app.set('trust proxy', 'loopback, 10.0.0.0/8');

Solution 2: Conditional trust based on environment

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

// Only trust proxy in production
if (process.env.NODE_ENV === 'production') {
  app.set('trust proxy', true);
}

app.get('/api/info', (req, res) => {
  res.json({
    ip: req.ip,
    protocol: req.protocol,
    hostname: req.hostname,
    secure: req.secure,
    ips: req.ips
  });
});

Solution 3: Nginx configuration

# nginx.conf
upstream nodejs {
    server 127.0.0.1:3000;
}

server {
    listen 443 ssl;
    server_name example.com;

    location / {
        proxy_pass http://nodejs;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
    }
}

Solution 4: Production-ready Express setup

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

// Security middleware
app.use(helmet());

// Trust proxy in production
if (process.env.NODE_ENV === 'production') {
  app.set('trust proxy', 1); // Trust first proxy
  
  // Redirect HTTP to HTTPS
  app.use((req, res, next) => {
    if (req.headers['x-forwarded-proto'] !== 'https') {
      return res.redirect(301, `https://${req.hostname}${req.url}`);
    }
    next();
  });
}

// Get real client IP
app.get('/api/client-info', (req, res) => {
  res.json({
    ip: req.ip,
    ips: req.ips,
    protocol: req.protocol,
    hostname: req.hostname,
    secure: req.secure
  });
});

// Rate limiting based on real IP
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,
  keyGenerator: (req) => req.ip // Uses real client IP
});

app.use('/api/', limiter);

Solution 5: Trust proxy options

// Trust all proxies (not recommended for production)
app.set('trust proxy', true);

// Trust only specific proxy
app.set('trust proxy', '10.0.0.1');

// Trust subnet
app.set('trust proxy', '10.0.0.0/16');

// Trust multiple proxies
app.set('trust proxy', ['loopback', '10.0.0.0/8', '172.16.0.0/12']);

// Trust by function
app.set('trust proxy', (ip) => {
  return ip === '127.0.0.1' || ip === '10.0.0.1';
});

Lessons Learned

  1. Never trust proxy headers in development - Only enable in production
  2. Set trust proxy to the number of proxies if behind a single proxy
  3. Use req.ip after configuring trust proxy to get the real client IP
  4. Update rate limiting to use req.ip for accurate client identification
  5. Test with curl to verify proxy headers are being read correctly

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.