← Course Index

Nginx as a Reverse Proxy

~25 min · Nginx · Nginx Cheat Sheet →

Ref
Primary Source
DigitalOcean — How to Configure Nginx as a Reverse Proxy

Step-by-step guide for setting up Nginx to proxy to your application. The definitive practical tutorial. DigitalOcean →

What is a Reverse Proxy?

Your Node.js app listens on port 3000. Nginx listens on port 80/443. Nginx receives every HTTP/HTTPS request from the internet and proxies it to your app. The client never connects directly to Node.js.

Internet Request to :443 Nginx Port 80 + 443 SSL · Headers · Routing Node.js App localhost:3000 Your application DB / Redis Backend services
Nginx sits between the internet and your app — handling SSL, routing, and security

Benefits of a reverse proxy:

Nginx Configuration Structure

# /etc/nginx/nginx.conf — main config
# /etc/nginx/sites-available/ — available site configs
# /etc/nginx/sites-enabled/  — symlinks to active configs
# /var/log/nginx/access.log  — all requests
# /var/log/nginx/error.log   — errors

# NEVER edit nginx.conf directly. Create files in sites-available/
# then symlink to sites-enabled/

A Production Nginx Config

# /etc/nginx/sites-available/yourapp.com

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

# HTTPS server
server {
    listen 443 ssl http2;
    server_name yourapp.com www.yourapp.com;

    # SSL certificates (managed by Certbot)
    ssl_certificate     /etc/letsencrypt/live/yourapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourapp.com/privkey.pem;

    # TLS settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    add_header Referrer-Policy "strict-origin-when-cross-origin";

    # Serve static files directly (fast)
    location /static/ {
        alias /var/www/yourapp/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Proxy everything else to Node.js
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;

        # Required headers
        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_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_read_timeout    60s;
        proxy_connect_timeout 10s;
        proxy_send_timeout    60s;
    }

    # Custom error pages
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
        internal;
    }
}

Enable & Test the Config

# Create the config file
sudo nano /etc/nginx/sites-available/yourapp.com

# Test for syntax errors (ALWAYS do this before reloading)
sudo nginx -t

# Enable the site (symlink to sites-enabled)
sudo ln -s /etc/nginx/sites-available/yourapp.com \
           /etc/nginx/sites-enabled/

# Reload Nginx (zero-downtime config reload)
sudo systemctl reload nginx

# View access logs
sudo tail -f /var/log/nginx/access.log

# View error logs
sudo tail -f /var/log/nginx/error.log
⚠️ Always run nginx -t first

A syntax error in your Nginx config will prevent reload and potentially take down your site. Always run sudo nginx -t before reloading. If it outputs "test is successful", it's safe to reload.

Multiple Apps on One Server

Nginx lets you host multiple apps on one EC2 instance — great for cost savings on small projects:

# /etc/nginx/sites-available/api.yourapp.com
server {
    listen 443 ssl http2;
    server_name api.yourapp.com;
    # ... ssl certs ...
    location / {
        proxy_pass http://localhost:3001;  # API on 3001
    }
}

# /etc/nginx/sites-available/admin.yourapp.com
server {
    listen 443 ssl http2;
    server_name admin.yourapp.com;
    # ... ssl certs ...
    location / {
        proxy_pass http://localhost:3002;  # Admin on 3002
    }
}

Check Your Understanding

1. Your Node.js app receives requests where req.ip always shows 127.0.0.1 (Nginx's IP) instead of the real client IP. What's the fix?
2. You update your Nginx config and run `sudo nginx -t`. It outputs errors. What should you do next?
3. Your app uses WebSockets for real-time features. Which Nginx directive is required?