DocumentationNginxReverse Proxies

Nginx Reverse Proxy Configuration

Basic Reverse Proxy Setup

Create Configuration File

# Create new site configuration
sudo nano /etc/nginx/sites-available/domain.com
 
# Or for conf.d directory
sudo nano /etc/nginx/conf.d/domain.com.conf

Basic Configuration

# Define upstream server
upstream project_upstream {
    server 127.0.0.1:3000;
}
 
# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name domain.com www.domain.com;
    return 301 https://$server_name$request_uri;
}
 
# HTTPS with proxy
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name domain.com www.domain.com;
 
    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
 
    location / {
        proxy_pass http://project_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        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_cache_bypass $http_upgrade;
    }
}

Multiple Upstream Servers (Load Balancing)

# Define upstream with multiple servers
upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}
 
server {
    listen 80;
    server_name example.com;
 
    location / {
        proxy_pass http://backend;
    }
}

Proxy with Caching

Configure Cache Path

# At top level of nginx.conf (outside server block)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=7d use_temp_path=off;
 
upstream project_upstream {
    server 127.0.0.1:3000;
}
 
server {
    listen 443 ssl http2;
    server_name domain.com;
 
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
 
    # Cache static files
    location ~* ^.+\.(js|css|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
        proxy_cache STATIC;
        proxy_pass http://project_upstream;
        proxy_cache_valid 200 7d;
        proxy_ignore_headers Cache-Control;
        expires 7d;
    }
 
    # Proxy everything else
    location / {
        proxy_pass http://project_upstream;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Node.js Application Proxy

upstream nodejs {
    server 127.0.0.1:3000;
}
 
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;
 
    location / {
        proxy_pass http://nodejs;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        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_cache_bypass $http_upgrade;
    }
}

Next.js / Nuxt.js Cache Configuration

# Cache configuration for Next.js
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=NEXT:10m inactive=7d use_temp_path=off;
 
upstream nextjs_upstream {
    server 127.0.0.1:3000;
}
 
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;
 
    # Cache Next.js static files
    location /_next/static {
        proxy_cache NEXT;
        proxy_pass http://nextjs_upstream;
        proxy_cache_valid 200 7d;
        proxy_ignore_headers Cache-Control;
    }
 
    # Cache public static files
    location /static {
        proxy_cache NEXT;
        proxy_pass http://nextjs_upstream;
        proxy_cache_valid 200 60m;
        proxy_ignore_headers Cache-Control;
    }
 
    # Proxy all other requests
    location / {
        proxy_pass http://nextjs_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        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;
    }
}

Python Application (Flask/Django)

upstream python_app {
    server 127.0.0.1:5000;
}
 
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;
 
    client_max_body_size 100M;
 
    location / {
        proxy_pass http://python_app;
        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 90;
    }
 
    # Serve static files directly
    location /static {
        alias /var/www/app/static;
        expires 30d;
    }
}

WebSocket Configuration

# For applications requiring WebSocket support
upstream websocket_backend {
    server 127.0.0.1:3000;
}
 
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;
 
    location / {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        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_read_timeout 86400;
    }
}

Multiple Applications

upstream app1 {
    server 127.0.0.1:3000;
}
 
upstream app2 {
    server 127.0.0.1:3001;
}
 
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;
 
    # Route to app1
    location /app1 {
        proxy_pass http://app1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
 
    # Route to app2
    location /app2 {
        proxy_pass http://app2;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
 
    # Default location
    location / {
        proxy_pass http://app1;
    }
}

Enable Configuration

# Create symlink to enable site
sudo ln -s /etc/nginx/sites-available/domain.com /etc/nginx/sites-enabled/
 
# Test configuration
sudo nginx -t
 
# Reload Nginx
sudo systemctl reload nginx

Security Headers

server {
    listen 443 ssl http2;
    server_name example.com;
 
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
 
    location / {
        proxy_pass http://upstream;
    }
}

Debugging

# View Nginx error log
tail -f /var/log/nginx/error.log
 
# Check upstream status
# Add to location block for debugging:
# error_page 502 503 504 /5xx.html;
 
# View active connections
ss -an | grep ESTABLISHED
 
# Test proxy connection
curl -v http://127.0.0.1:3000

1. Create new domain

cd /etc/nginx/sites-available
sudo nano domain.com
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=7d use_temp_path=off;

upstream project_upstream {
  server 127.0.0.1:3000;
}

server {
    listen 80;
    server_name domain.com;
    rewrite ^(.*) https://domain.com$1 permanent;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    root /usr/share/nginx/html/project;
    
    ssl on;
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

    server_name domain.com;
    server_tokens off;

    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;

    location / {
        proxy_pass http://project_upstream;
    }
}

{% hint style=“info” %} Change domain.com, port, project dir to your project info. {% endhint %}

2. Cache file for nuxtjs - nextjs

location /_next/static {
    proxy_cache STATIC;
    proxy_pass http://project_upstream;
}

location /static {
    proxy_cache STATIC;
    proxy_ignore_headers Cache-Control;
    proxy_cache_valid 60m;
    proxy_pass http://project_upstream;
}

location / {
    proxy_pass http://project_upstream;
}