Knowledge Base

How to properly configure a Nginx reverse proxy for a Node.js application?

Posted by user123 on 2023-10-27 10:30 AM | nginx, nodejs, reverse proxy, configuration

Hello everyone,

I'm setting up a Node.js application and want to use Nginx as a reverse proxy for it. I've read some documentation, but I'm not entirely sure about the best practices for configuring Nginx to handle SSL, load balancing (if needed later), and health checks.

Specifically, I'm looking for guidance on:

  • Basic Nginx configuration to forward requests to a Node.js app running on a specific port (e.g., 3000).
  • Setting up SSL/TLS with Let's Encrypt.
  • Configuring headers like X-Forwarded-For, X-Real-IP, and Host correctly.
  • Any common pitfalls or security considerations.

Here's a snippet of what I have so far:


server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}
                

Any help or examples would be greatly appreciated!

Answers

expert_admin 2023-10-27 11:15 AM

This is a common and important setup! Your initial configuration is a good starting point. Here are some refined best practices and a more complete example, including SSL:

SSL Configuration with Let's Encrypt

First, ensure you have Certbot installed and configured for your domain.


# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

# HTTPS Server Block
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    # Recommended SSL settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1; # Important for keepalive connections
        proxy_set_header Upgrade $http_upgrade; # For WebSocket support
        proxy_set_header Connection 'upgrade';
        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_read_timeout 0; # Prevent timeouts on long polling or WebSocket connections
    }

    # Optional: serve static files directly from Nginx for better performance
    # location /static/ {
    #     alias /path/to/your/node/app/public/static/;
    #     expires 30d;
    # }
}
                        

Explanation:

  • The first server block handles the HTTP to HTTPS redirect.
  • The second server block listens on 443 ssl, specifies SSL certificate paths, and includes robust SSL settings for security.
  • proxy_http_version 1.1; is crucial for enabling keep-alive connections to your Node.js app, improving performance.
  • proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection 'upgrade'; are essential for WebSocket support.
  • proxy_read_timeout 0; can be useful for applications that use long polling or WebSockets to prevent Nginx from closing the connection prematurely.

Load Balancing (Future Proofing)

If you need to scale, you can add an upstream block:


upstream nodejs_app {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    # server backend_ip:port;
}

server {
    # ... SSL and other settings ...

    location / {
        proxy_pass http://nodejs_app; # Use the upstream name here
        # ... proxy_set_header directives ...
    }
}
                        

Nginx will then distribute requests among the listed servers. You can add health checks for more advanced setups.

Security Considerations:

  • Always keep Nginx and your SSL certificates up to date.
  • Limit the request body size if your application doesn't need large uploads: client_max_body_size 10M;
  • Consider using Nginx's security modules or rate limiting if you anticipate heavy traffic or attacks.

This setup should provide a solid foundation for your Node.js application behind Nginx.

dev_learner 2023-10-27 11:45 AM

Thanks for the detailed answer, expert_admin! I was having trouble with WebSockets.

I have a follow-up question: what if my Node.js app is running inside a Docker container on the same host, but on a different IP or port exposed to the host?

For example, the container exposes port 8080, but it's accessible from the host at 172.17.0.2:8080 (Docker's internal network).

How would the proxy_pass directive change?

Have an answer?