How to Properly Configure Nginx proxy_pass with Node.js HTTP Server Using UNIX Domain Sockets


2 views

When setting up Nginx to proxy requests to a Node.js server via UNIX domain sockets, several configuration aspects need careful attention. The 502 Bad Gateway error typically indicates a connection failure between Nginx and your Node.js application.

Here's a working configuration that addresses both Nginx and Node.js setup:

// Node.js server setup
const http = require('http');
const fs = require('fs');

// Clean up existing socket if present
try { fs.unlinkSync('/tmp/app.socket'); } catch(e) {}

http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello from Node.js via UNIX socket!\n');
}).listen('/tmp/app.socket', () => {
  console.log('Node.js server listening on UNIX socket');
});

// Set appropriate permissions
fs.chmod('/tmp/app.socket', 0777, (err) => {
  if (err) throw err;
});

The most reliable approach combines upstream declaration with proper permissions:

upstream nodejs_socket {
  server unix:/tmp/app.socket;
}

server {
  listen 80;
  server_name localhost;

  location / {
    proxy_pass http://nodejs_socket;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # Important for UNIX socket connections
    proxy_http_version 1.1;
    proxy_set_header Connection "";
  }
}

The permission denied error (13) suggests Nginx's worker process can't access the socket file. Solutions include:

  • Ensure both processes run under the same user
  • Set appropriate socket file permissions (0777 for testing)
  • Check apparmor/selinux policies if enforced

When troubleshooting, verify these aspects:

# Check socket file existence and permissions
ls -la /tmp/app.socket

# Verify Nginx worker process user
ps aux | grep nginx

# Test direct socket connection
curl --unix-socket /tmp/app.socket http://localhost/

For newer Nginx versions, you can use this direct approach:

server {
  listen 80;
  
  location / {
    proxy_pass http://unix:/tmp/app.socket:/;
    proxy_set_header Host $http_host;
  }
}

Remember that the trailing colon and slash in unix:/tmp/app.socket:/ are crucial for proper request URI handling.


Setting up Nginx to proxy requests to a Node.js application via UNIX domain sockets should be straightforward, but there are several common pitfalls that can lead to 502 Bad Gateway errors. Let's examine a working configuration that addresses both the Nginx setup and Node.js implementation.

Here's the correct way to configure Nginx for UNIX socket proxying:

upstream nodejs_unix {
    server unix:/tmp/app.socket;
}

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://nodejs_unix;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

The Node.js server needs proper socket handling and permissions:

const http = require('http');
const fs = require('fs');

const socketPath = '/tmp/app.socket';

// Clean up socket if it exists from previous runs
try { fs.unlinkSync(socketPath); } catch (e) {}

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello from UNIX socket!\n');
});

server.listen(socketPath, () => {
    console.log(Server running at ${socketPath});
    // Set appropriate permissions
    fs.chmodSync(socketPath, '777');
});

The most common issue is permission errors between Nginx and the socket file. Consider these points:

  • Nginx worker processes must have read/write permissions to the socket
  • The directory (/tmp) must allow socket creation
  • Best practice is to use a dedicated directory with proper ownership

When encountering issues, check these areas:

# Check socket permissions
ls -la /tmp/app.socket

# Verify Nginx error logs
tail -f /var/log/nginx/error.log

# Test socket connectivity directly
curl --unix-socket /tmp/app.socket http://localhost/

Using UNIX sockets instead of TCP offers several benefits:

  • Lower latency (no TCP overhead)
  • No port conflicts
  • Better security (filesystem permissions)
  • Higher throughput for local communications

For Express.js applications, the setup is similar but requires proper headers:

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

app.get('/', (req, res) => {
    res.send('Express via UNIX socket');
});

app.listen('/tmp/express.socket', () => {
    console.log('Express server listening on socket');
});