Configuring NGINX as Reverse Proxy for Local SMTP Server on Non-Standard Port


5 views

When working with SMTP servers in development environments, we often face port binding limitations. The Node.js smtp-server module typically needs to run on privileged ports (like 25) which requires root access - a security risk we want to avoid. The solution involves:

  • Running SMTP on high-numbered ports (2500 in this case)
  • Using NGINX to proxy port 25 traffic
  • Maintaining raw TCP passthrough without protocol interpretation

Unlike HTTP proxying, SMTP requires NGINX's stream module for raw TCP forwarding. First ensure your NGINX includes stream support:

nginx -V 2>&1 | grep -o with-stream

If no output, recompile NGINX with --with-stream or install the stream module package for your distribution.

Create a new configuration file at /etc/nginx/nginx.conf or include it in your existing setup:

stream {
    server {
        listen 25;
        proxy_pass 127.0.0.1:2500;
        proxy_protocol on;
        proxy_buffer_size 16k;
    }
}

When exposing port 25, consider these hardening measures:

stream {
    server {
        listen 25;
        proxy_pass 127.0.0.1:2500;
        proxy_timeout 3m;
        proxy_connect_timeout 30s;
        allow 192.168.1.0/24;
        deny all;
    }
}

After reloading NGINX (nginx -s reload), test with:

telnet your-server-ip 25
EHLO example.com

You should see responses from your Node.js SMTP server. Monitor connections with:

sudo ngrep -d any port 25

If your SMTP server handles TLS directly, NGINX can pass through encrypted traffic:

stream {
    server {
        listen 25;
        listen 587;
        proxy_pass 127.0.0.1:2500;
        proxy_ssl off; # Crucial for TLS passthrough
    }
}
  • Connection refused: Verify Node.js server is running on 2500
  • Permission denied: Check SELinux/AppArmor for port binding
  • No response: Ensure stream block exists in main nginx.conf

Remember that some cloud providers block port 25 by default - check your VPS firewall rules.


When developing SMTP server applications (like those using Node.js's smtp-server module), Linux systems prevent non-root users from binding to privileged ports below 1024. This creates a deployment hurdle for developers wanting to test SMTP services on standard port 25.

While NGINX is primarily an HTTP server, its stream module allows raw TCP proxying - perfect for protocol-agnostic traffic forwarding. This approach offers several advantages:

  • No need to run application as root
  • Preserves original SMTP protocol semantics
  • Leverages existing NGINX infrastructure
  • Enables future load balancing if needed

First, ensure your NGINX includes the stream module (standard in most distributions). Then create or modify /etc/nginx/nginx.conf:

# Main NGINX configuration
user www-data;
worker_processes auto;

# Include stream module configuration
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
}

# HTTP block remains unchanged
http {
    ...
}

# Critical addition - TCP proxy configuration
stream {
    server {
        listen 25;
        proxy_pass localhost:2500;
        proxy_protocol on;
    }
}

After reloading NGINX (sudo systemctl reload nginx), verify the proxy works:

# Test connection
telnet localhost 25
# Should connect to your SMTP server on port 2500

# Verify NGINX is listening
sudo ss -tulnp | grep ':25'
# Should show nginx process

For real-world deployment, consider these enhancements:

stream {
    server {
        listen 25 so_keepalive=on;
        proxy_pass 127.0.0.1:2500;
        proxy_timeout 1h;
        proxy_connect_timeout 10s;
        
        # Optional TLS passthrough
        proxy_ssl on;
        proxy_ssl_protocols TLSv1.2 TLSv1.3;
    }
}

If connections fail, check:

  • Firewall rules (sudo ufw status)
  • SELinux contexts
  • Port availability (netstat -tuln)
  • NGINX error logs (/var/log/nginx/error.log)