Skip to main content
دروس12 min read

إعداد n8n Nginx Reverse Proxy الكامل (SSL، WebSocket، رؤوس الأمان)

تكوين Nginx جاهز للإنتاج لـ n8n مع SSL عبر Let's Encrypt ودعم WebSocket ورؤوس الأمان وتحديد المعدل وشبكة Docker.

بقلم AutomationVPS

Why Most n8n Nginx Configs Are Incomplete

Search for "n8n nginx config" and you'll find dozens of examples. Most of them are broken in subtle ways: WebSocket connections fail silently, webhooks return HTTP URLs instead of HTTPS, large payloads get rejected, or long-running workflows time out.

The problem is that n8n isn't a simple web app. It needs WebSocket connections for the editor's real-time features, long timeout windows for workflow executions, large body limits for webhook payloads with file attachments, and proper header forwarding so it knows its own public URL.

This guide gives you a complete, production-tested Nginx configuration that handles all of these -- plus SSL with automatic renewal, security headers, and rate limiting.

Prerequisites

Before you start, you need:

  • A VPS with n8n running (Docker or bare metal)
  • A domain name pointed to your VPS IP (e.g., n8n.yourdomain.com)
  • Nginx installed: sudo apt install nginx
  • Certbot for Let's Encrypt SSL: sudo apt install certbot python3-certbot-nginx

The Complete Production Config

Here's the full Nginx server block. We'll break down every section after:

# Rate limiting zone — 10 requests/second per IP
limit_req_zone $binary_remote_addr zone=n8n_ratelimit:10m rate=10r/s;

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

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name n8n.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

# Main HTTPS server
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name n8n.yourdomain.com;

    # --- SSL Configuration ---
    ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_stapling on;
    ssl_stapling_verify on;

    # --- Security Headers ---
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # --- Body Size (for webhook payloads with attachments) ---
    client_max_body_size 50m;

    # --- Webhook Endpoint (rate limited) ---
    location /webhook/ {
        limit_req zone=n8n_ratelimit burst=20 nodelay;

        proxy_pass http://127.0.0.1:5678;
        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_set_header X-Forwarded-Host $host;

        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
    }

    location /webhook-test/ {
        proxy_pass http://127.0.0.1:5678;
        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_set_header X-Forwarded-Host $host;

        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
    }

    # --- Main Application (WebSocket + HTTP) ---
    location / {
        proxy_pass http://127.0.0.1:5678;

        # Forward client identity
        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_set_header X-Forwarded-Host $host;

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

        # Timeouts for long-running operations
        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 3600s;

        # Disable buffering for real-time events
        proxy_buffering off;
        proxy_cache off;
    }
}

Section-by-Section Breakdown

Rate Limiting

limit_req_zone $binary_remote_addr zone=n8n_ratelimit:10m rate=10r/s;

This creates a rate limiting zone that allows 10 requests per second per IP address. The 10m allocates 10 MB of shared memory for tracking IP addresses (enough for ~160,000 unique IPs).

The rate limit is applied only to the /webhook/ location, not the entire application. This protects your webhook endpoints from abuse without interfering with the n8n editor's normal operation.

The burst=20 nodelay in the webhook location allows short bursts of up to 20 requests before rate limiting kicks in, and nodelay processes burst requests immediately rather than queuing them.

WebSocket Upgrade Map

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

This is essential for n8n's real-time editor features. The map directive dynamically sets the Connection header based on whether the request is a WebSocket upgrade or a regular HTTP request.

Without this, you'd need to hardcode Connection "upgrade" on every request -- which breaks regular HTTP responses. The map handles both cases correctly.

⚠️

If WebSocket connections fail, the n8n editor will load but "Listen for Test Event" in webhook nodes will never receive data. The editor appears to work while test webhooks silently break. This is the most common symptom of missing WebSocket configuration.

SSL Configuration

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:...';

We restrict to TLS 1.2 and 1.3 only (TLS 1.0 and 1.1 are deprecated and insecure). The cipher suite prioritizes ECDHE for forward secrecy and AES-GCM for authenticated encryption.

ssl_stapling enables OCSP stapling, which speeds up SSL handshakes by including the certificate validation response directly from Nginx instead of requiring the client to contact the certificate authority.

Security Headers

Each header serves a specific purpose:

  • Strict-Transport-Security (HSTS) -- tells browsers to always use HTTPS for this domain for one year. includeSubDomains extends this to all subdomains.
  • X-Frame-Options -- prevents your n8n instance from being embedded in iframes on other sites (clickjacking protection).
  • X-Content-Type-Options -- stops browsers from MIME-type sniffing, forcing them to respect the declared content type.
  • X-XSS-Protection -- enables the browser's built-in XSS filter.
  • Referrer-Policy -- controls how much referrer information is sent with requests.

Proxy Headers

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;

These two headers are why most n8n webhook issues exist. Without X-Forwarded-Proto, n8n doesn't know the connection is HTTPS -- so webhook URLs show http:// instead of https://. Without X-Forwarded-Host, n8n uses its internal hostname (often localhost in Docker) instead of your public domain.

Both of these must be paired with N8N_PROXY_HOPS=1 in your n8n environment, or n8n will ignore them entirely.

Timeouts

proxy_read_timeout 3600s;  # 1 hour for the main app
proxy_read_timeout 300s;   # 5 minutes for webhooks

The main application location has a 1-hour read timeout. This is necessary because n8n keeps WebSocket connections open for the editor, and long-running workflow executions can take minutes to complete. Without a generous timeout, Nginx closes the connection and n8n reports a failed execution even though the workflow completed.

Webhook endpoints use a shorter 5-minute timeout. Webhook requests should complete quickly; if they're taking more than 5 minutes, you have a workflow design issue.

Buffering

proxy_buffering off;
proxy_cache off;

Disabling buffering ensures that n8n's real-time events (execution progress, test webhook data) are sent to the browser immediately. With buffering enabled, Nginx accumulates response data before forwarding it, which adds latency to the editor's real-time features.

Setting Up SSL with Let's Encrypt

Get a free SSL certificate:

# Get the certificate (Nginx must be running with the HTTP config)
sudo certbot --nginx -d n8n.yourdomain.com

# Test automatic renewal
sudo certbot renew --dry-run

Certbot adds a cron job automatically. Certificates renew every 60-90 days. If renewal fails, your webhooks will break silently (external services will get SSL errors but you won't see anything in n8n's logs).

Set up a monitoring check:

# Add to crontab: alert if certificate expires within 14 days
0 9 * * * certbot certificates 2>&1 | grep -A2 "n8n.yourdomain.com" | grep "VALID" || echo "SSL cert expiring soon" | mail -s "SSL Alert" [email protected]

n8n Environment Variables

Your n8n Docker Compose must include these environment variables for the Nginx proxy to work correctly:

services:
  n8n:
    image: n8nio/n8n:latest
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      - N8N_PROXY_HOPS=1
      - N8N_SECURE_COOKIE=true
    volumes:
      - n8n_data:/home/node/.n8n

Key details:

  • 127.0.0.1:5678:5678 -- binds n8n to localhost only. Without 127.0.0.1:, Docker publishes the port on all interfaces, making n8n directly accessible on port 5678 and bypassing Nginx entirely.
  • WEBHOOK_URL -- the trailing slash matters. Include it.
  • N8N_PROXY_HOPS=1 -- tells n8n to trust one layer of proxy headers.
  • N8N_SECURE_COOKIE=true -- marks session cookies as secure (HTTPS only).

The most common mistake: binding to 0.0.0.0:5678 instead of 127.0.0.1:5678. This makes n8n accessible directly on port 5678, bypassing your Nginx security headers, rate limiting, and SSL. Always bind to localhost when using a reverse proxy.

Docker Network Configuration

If n8n and Nginx are both running in Docker, 127.0.0.1 in the proxy_pass won't work because each container has its own localhost. Use Docker's internal networking instead:

services:
  n8n:
    image: n8nio/n8n:latest
    networks:
      - n8n-network
    # Do NOT publish ports when using Docker networking
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      - N8N_PROXY_HOPS=1

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/n8n.conf
      - /etc/letsencrypt:/etc/letsencrypt:ro
    networks:
      - n8n-network

networks:
  n8n-network:
    driver: bridge

In this setup, change proxy_pass in your Nginx config from http://127.0.0.1:5678 to http://n8n:5678 (using the Docker service name).

Testing Your Configuration

After setting everything up, verify each component:

# 1. Test Nginx config syntax
sudo nginx -t

# 2. Reload Nginx
sudo systemctl reload nginx

# 3. Test HTTPS access
curl -I https://n8n.yourdomain.com

# 4. Test WebSocket upgrade
curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGVzdA==" \
  https://n8n.yourdomain.com/rest/push

# 5. Test webhook endpoint
curl -X POST https://n8n.yourdomain.com/webhook/test-path \
  -H "Content-Type: application/json" \
  -d '{"test": true}'

# 6. Test security headers
curl -I https://n8n.yourdomain.com | grep -E "Strict|X-Frame|X-Content|X-XSS|Referrer"

# 7. Verify n8n sees correct protocol
docker exec n8n env | grep -E "WEBHOOK|PROXY|PROTOCOL"

Expected results:

  • Step 3: HTTP 200 with HTTPS redirect working
  • Step 4: HTTP 101 Switching Protocols (confirms WebSocket works)
  • Step 5: HTTP 404 (expected -- no workflow registered at that path, but proves Nginx forwards to n8n)
  • Step 6: All five security headers present

Hostinger

Hostinger VPS includes full root access for Nginx + SSL configuration. KVM 1 at $6.49/mo with NVMe storage for fast n8n performance.

Visit Hostinger

* Affiliate link — we may earn a commission at no extra cost to you.

Common Errors and Fixes

ErrorCauseFix
502 Bad Gatewayn8n not running or wrong proxy_pass addressCheck docker ps, verify n8n is up and port matches
504 Gateway TimeoutWorkflow takes longer than proxy timeoutIncrease proxy_read_timeout
WebSocket fails silentlyMissing map directive or upgrade headersAdd the map $http_upgrade block and WebSocket headers
Webhook URLs show http://Missing X-Forwarded-Proto or N8N_PROXY_HOPSAdd both the header and the env variable
Webhook URLs show localhostMissing X-Forwarded-Host or WEBHOOK_URLAdd the header and set WEBHOOK_URL in n8n env
413 Request Entity Too LargePayload exceeds client_max_body_sizeIncrease to 50m or higher
n8n accessible on port 5678 directlyDocker port binding on 0.0.0.0Change to 127.0.0.1:5678:5678
SSL certificate errorsExpired cert or wrong pathRun certbot renew and check certificate paths

VPS Recommendations for n8n + Nginx

Running Nginx alongside n8n adds minimal overhead (~20-50 MB RAM). Any VPS suitable for n8n handles the reverse proxy easily:

  • Contabo VPS 1 ($4.50/mo): 8 GB RAM, 4 vCPU -- handles n8n + Nginx + PostgreSQL comfortably
  • Hostinger KVM 1 ($6.49/mo): 4 GB RAM, NVMe storage, easy management panel
  • DigitalOcean ($6-12/mo): One-click Nginx marketplace image, built-in monitoring
  • Vultr ($5-12/mo): 32 locations for optimal latency to your users

DigitalOcean

DigitalOcean's one-click Nginx + Let's Encrypt Droplet gets you a reverse proxy ready in 60 seconds. Pair with n8n for a complete automation stack.

Visit DigitalOcean

* Affiliate link — we may earn a commission at no extra cost to you.

Conclusion

A properly configured Nginx reverse proxy is essential for any production n8n deployment. It provides SSL termination, WebSocket support, security headers, rate limiting, and payload handling that n8n can't do on its own.

Copy the complete config from this guide, replace n8n.yourdomain.com with your domain, run Certbot for SSL, and set the correct n8n environment variables. Test all five components (HTTPS, WebSocket, webhooks, headers, and port binding) and you'll have a bulletproof setup that handles everything production throws at it.

هل أنت مستعد للبدء بالأتمتة؟ احصل على VPS اليوم.

ابدأ استخدام استضافة Hostinger VPS اليوم. أسعار خاصة متاحة.

احصل على Hostinger VPS

* رابط تابع — قد نحصل على عمولة دون تكلفة إضافية عليك

#n8n#nginx#reverse-proxy#ssl#security