Nginx Reverse Proxy for Node.js (Config Snippet + Test with curl)

Running a Node.js app behind Nginx is a battle-tested pattern: Nginx handles TLS, static files, gzip, and connection upgrades (for WebSockets), while your Node app focuses on business logic. This guide gives you a clean, production-ready Nginx config and shows how to test it with curl.


What you’ll set up

  • A Node.js app listening on 127.0.0.1:3000
  • Nginx reverse proxy on port 80/443
  • WebSocket support
  • Gzip compression & basic hardening
  • Simple tests with curl

Works on Ubuntu/Debian and CentOS/RHEL. Adjust paths if your distro uses different Nginx locations.


1) Minimal Node.js app (for testing)

Create a tiny server to verify the proxy:

mkdir -p /var/www/node-app && cd /var/www/node-app
cat > server.js <<'EOF'
const http = require('http');

const server = http.createServer((req, res) => {
  // Simple health route
  if (req.url === '/health') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    return res.end(JSON.stringify({ ok: true, via: 'node', time: new Date().toISOString() }));
  }

  // Echo headers (useful for proxy tests)
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({
    url: req.url,
    headers: req.headers,
    message: 'Hello from Node behind Nginx!'
  }));
});

server.listen(3000, '127.0.0.1', () => {
  console.log('Node app listening on http://127.0.0.1:3000');
});
EOF

node server.js

You should see: Node app listening on http://127.0.0.1:3000


2) (Optional) Keep Node running with systemd

sudo tee /etc/systemd/system/node-app.service >/dev/null <<'EOF'
[Unit]
Description=Node.js Example App
After=network.target

[Service]
WorkingDirectory=/var/www/node-app
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=3
Environment=NODE_ENV=production
User=www-data
Group=www-data

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now node-app
sudo systemctl status node-app --no-pager

On CentOS/RHEL, the Node path is usually /usr/bin/node too; user/group may be nginx instead of www-data.


3) Nginx reverse proxy config (HTTP)

Create a site config (Debian/Ubuntu style):

sudo mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
sudo tee /etc/nginx/sites-available/node-proxy.conf >/dev/null <<'EOF'
# Replace example.com with your domain
server {
    listen 80;
    server_name example.com www.example.com;

    # Gzip for JSON/JS/CSS
    gzip on;
    gzip_types text/plain application/json application/javascript text/css;
    gzip_min_length 1024;

    # Proxy timeouts (tune as needed)
    proxy_connect_timeout 5s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    send_timeout 60s;

    # Forward real client IPs
    set $upstream http://127.0.0.1:3000;

    location / {
        proxy_pass $upstream;

        # Preserve Host and IP info
        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;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Don't buffer SSE/WebSockets too aggressively
        proxy_buffering off;
    }

    # Health path can be cached off for clarity
    location = /health {
        proxy_pass $upstream;
        proxy_set_header Host $host;
        proxy_buffering off;
    }

    # Basic hardening
    add_header X-Frame-Options SAMEORIGIN always;
    add_header X-Content-Type-Options nosniff always;

    # Map for Connection upgrade (place in http{} block if in main nginx.conf)
}
EOF

Create a small map to handle Connection header for WS upgrades. Put this once in your global http {} block (e.g., /etc/nginx/nginx.conf), outside the server block:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Enable and test:

sudo ln -s /etc/nginx/sites-available/node-proxy.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

CentOS/RHEL path: often a single /etc/nginx/nginx.conf with server {} blocks in /etc/nginx/conf.d/*.conf. If so, save as /etc/nginx/conf.d/node-proxy.conf and skip the symlink step.


4) Add HTTPS (Let’s Encrypt)

If your DNS points to the server, you can set up TLS quickly:

# Install Certbot
# Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx

# CentOS/RHEL (Stream 8/9; may be 'dnf' and package names differ slightly):
# sudo dnf install -y certbot python3-certbot-nginx

# Obtain and auto-configure SSL for the server block with server_name example.com
sudo certbot --nginx -d example.com -d www.example.com

# Auto renewal (usually installed by default)
sudo systemctl status certbot.timer --no-pager

Certbot will add listen 443 ssl http2; and ssl_certificate lines for you.


5) Test with 

curl

A. Test that Nginx reaches Node

# Replace example.com with your domain or server IP (if HTTP only for now)
curl -i http://example.com/health

Expected:

  • HTTP/1.1 200 OK (or HTTP/2 200 if HTTPS)
  • JSON body like: {“ok”:true,”via”:”node”,”time”:”…”}
  • Response header server: nginx (from Nginx) and body says it came from Node

B. Check headers forwarded by Nginx

curl -i http://example.com/ -H "X-Demo: test123"

Look for:

  • Host: example.com
  • x-demo: test123
  • x-forwarded-proto: http (or https if TLS enabled)
  • x-real-ip set to your client IP

C. Test HTTPS (after Certbot)

curl -I https://example.com/health

You should see:

  • HTTP/2 200
  • TLS certificate fields if you add -v

D. (Optional) WebSocket sanity test

If your Node app upgrades connections at /ws, you can verify the handshake:

curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Host: example.com" \
  -H "Origin: https://example.com" \
  http://example.com/ws

You should get 101 Switching Protocols if your backend handles WS.


6) Common pitfalls & fixes

  • 502 Bad Gateway
    • Node app not running or wrong upstream port/host.
    • SELinux (CentOS) may block Nginx from proxying: setsebool -P httpd_can_network_connect 1
  • WebSockets not upgrading
    • Missing proxy_http_version 1.1, Upgrade, and Connection headers.
    • The map $http_upgrade $connection_upgrade must be in http{}.
  • Wrong client IP
    • Ensure proxy_set_header X-Real-IP $remote_addr; and X-Forwarded-For.
  • TLS issues
    • DNS not pointing to server before running Certbot.
    • Port 80/443 blocked by firewall.

7) Full example (drop-in) for 

/etc/nginx/conf.d/node-proxy.conf

# Place this map ONCE in /etc/nginx/nginx.conf inside http { }:
# map $http_upgrade $connection_upgrade { default upgrade; '' close; }

server {
    listen 80;
    server_name example.com www.example.com;

    gzip on;
    gzip_types text/plain application/json application/javascript text/css;
    gzip_min_length 1024;

    set $upstream http://127.0.0.1:3000;

    location / {
        proxy_pass $upstream;
        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_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_buffering off;
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    location = /health {
        proxy_pass $upstream;
        proxy_set_header Host $host;
        proxy_buffering off;
    }

    add_header X-Frame-Options SAMEORIGIN always;
    add_header X-Content-Type-Options nosniff always;
}

Reload Nginx after changes:

sudo nginx -t && sudo systemctl reload nginx

Final checklist

  • Node app runs on 127.0.0.1:3000 (or your chosen port)
  • Nginx config enabled and tested (nginx -t)
  • Health endpoint returns 200 via Nginx
  • TLS installed with Certbot (optional but recommended)
  • curl tests show correct headers and status codes

Tags: Nginx, Node.js, Reverse Proxy, WebSockets, Linux, CentOS, Ubuntu, DevOps

Meta Description: Learn how to put Node.js behind Nginx with a production-ready reverse proxy config, WebSocket support, and quick tests using curl.


Posted

in

by

Comments

4 responses to “Nginx Reverse Proxy for Node.js (Config Snippet + Test with curl)”

  1. slotvip ph Avatar

    Excellent production-ready setup! We’ve been using similar Nginx reverse proxy configurations at slotvip ph online casino for our real-time gaming infrastructure. The WebSocket upgrade section is particularly valuable for live dealer streaming reliability.

  2. winbet ro Avatar

    Alright Bucharest buddies, just checked out Winbet RO and gotta say, not too shabby! The site’s got a decent selection and seems legit. Worth a punt for a Saturday night flutter. Check it out here! winbet ro

  3. casibom güvenli giriş Avatar

    Secure login is a must! Casibom’s got it covered. Always use the official link to avoid scams. Stay safe and play on! Safe login here casibom güvenli giriş

  4. vn123 win Avatar

    Alright, let’s get that ‘vn123 win’! I’m feeling lucky. Gonna check this out and see if I can strike gold. Come claim the win here: vn123 win

Leave a Reply

Your email address will not be published. Required fields are marked *