NGINX Reverse Proxy KB (1–2GB RAM) — Best Tweaks

Goals

  • Handle bursts safely on low RAM

  • Keep concurrency realistic (thousands, not hundreds of thousands)

  • Avoid swap thrashing / OOM

  • Get real client IP behind Cloudflare

  • Keep config minimal and stable


0) First Principles (don’t skip)

What limits you on 1–2GB is usually:

  1. RAM per connection (keepalive + TLS + buffers)

  2. CPU (TLS, HTTP/2, gzip, upstream latency)

  3. FD limits (only if left at default 1024)

  4. Backlog / SYN queue (bursts & attacks)

On low RAM, you must:

  • Keep timeouts short

  • Keep buffers moderate

  • Keep worker_connections sane

  • Ensure systemd LimitNOFILE is high


1) OS / systemd must-haves

1.1 Raise NGINX open files (FD limit)

Check:

ulimit -n
cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep -i "open files"

If nginx shows 1024 soft, fix via systemd:

mkdir -p /etc/systemd/system/nginx.service.d

cat >/etc/systemd/system/nginx.service.d/override.conf <<'EOF'
[Service]
LimitNOFILE=100000
EOF

systemctl daemon-reload
systemctl restart nginx

1.2 Swap (safety net)

  • For 1GB RAM: 2GB swap is fine

  • For 1.5–2GB RAM: 1.5–2GB swap is fine
    Swap is for stability, not capacity.

1.3 VM tuning (safe + proven)

cat >/etc/sysctl.d/99-nginx-memory.conf <<'EOF'
vm.swappiness=10
vm.vfs_cache_pressure=50
EOF

sysctl --system

1.4 Network tuning (safe for reverse proxy bursts)

cat >/etc/sysctl.d/99-nginx-network.conf <<'EOF'
# Faster cleanup of closed connections
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1

# Larger accept queue for bursts
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Better SYN flood handling
net.ipv4.tcp_syncookies = 1
EOF

sysctl --system

Verify:

sysctl net.ipv4.tcp_fin_timeout net.core.somaxconn net.ipv4.tcp_max_syn_backlog

2) Global NGINX config (/etc/nginx/nginx.conf)

Use this as your minimal best-practice global config for 1–2GB reverse proxy:

user www-data;
worker_processes auto;
worker_cpu_affinity auto;

pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;

include /etc/nginx/modules-enabled/*.conf;

worker_rlimit_nofile 100000;

events {
    use epoll;
    worker_connections 8192;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    types_hash_max_size 2048;
    server_tokens off;
    server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Cloudflare Real IP (global)
    include /etc/nginx/snippets/cloudflare-realip.conf;

    # Low-RAM safe timeouts
    reset_timedout_connection on;

    keepalive_timeout 10s;
    keepalive_requests 1000;

    client_header_timeout 10s;
    client_body_timeout 10s;
    send_timeout 10s;

    client_max_body_size 50m;

    # Moderate buffers (important for low RAM)
    client_body_buffer_size 16k;
    client_header_buffer_size 1k;
    large_client_header_buffers 2 8k;

    # FD cache
    open_file_cache max=20000 inactive=30s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    # Logging format (helps debug upstream latency)
    log_format main
        '$remote_addr - $remote_user [$time_local] '
        '"$request" $status $body_bytes_sent '
        '"$http_referer" "$http_user_agent" '
        'rt=$request_time uct=$upstream_connect_time '
        'uht=$upstream_header_time urt=$upstream_response_time '
        'ua="$upstream_addr" us="$upstream_status"';

    access_log /var/log/nginx/access.log main;

    # SSL defaults (certs live in server blocks)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_timeout 1h;
    ssl_session_tickets off;
    ssl_ecdh_curve X25519:secp256r1;

    resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 valid=300s ipv6=off;
    resolver_timeout 5s;

    # Gzip (safe defaults)
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_min_length 1024;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types
        text/plain text/css text/xml text/javascript
        application/javascript application/json application/xml
        application/rss+xml image/svg+xml;

    # WebSocket helper (safe even if unused)
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    # Reverse proxy defaults
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    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_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 30s;

    proxy_buffering on;
    proxy_buffer_size 16k;
    proxy_buffers 8 16k;
    proxy_busy_buffers_size 32k;
    proxy_max_temp_file_size 0;

    # Security headers
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Reload:

nginx -t && systemctl reload nginx

3) Cloudflare Real IP (required behind Cloudflare)

Create:

mkdir -p /etc/nginx/snippets

cat >/etc/nginx/snippets/cloudflare-realip.conf <<'EOF'
real_ip_header CF-Connecting-IP;
real_ip_recursive on;

# Cloudflare IPv4 ranges
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;

# Cloudflare IPv6 ranges (keep even if server IPv6 disabled)
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
EOF

Test:

nginx -t && systemctl reload nginx

4) Vhost template (reverse proxy + backlog + websocket safe)

Backlog must be in listen lines inside server {}

Example vhost:

server {
    listen 80 backlog=65535;
    listen 443 ssl http2 backlog=65535;

    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/lego-ip/fullchain.crt;
    ssl_certificate_key /etc/nginx/ssl/lego-ip/privkey.key;

    # Optional: verify real IP quickly
    location = /ip {
        default_type text/plain;
        return 200 "remote_addr=$remote_addr\ncf=$http_cf_connecting_ip\nxff=$http_x_forwarded_for\n";
    }

    location / {
        proxy_pass http://127.0.0.1:8080;

        # WebSocket safe headers (won’t break normal HTTP)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

Reload:

nginx -t && systemctl reload nginx

5) Recommended values (for 1–2GB)

NGINX

  • worker_processes auto

  • worker_connections 8192 (start here)

  • If stable + enough RAM: try 16384 (but test)

  • worker_rlimit_nofile 100000 (and systemd LimitNOFILE match)

Timeouts

  • keepalive_timeout 10s (important)

  • proxy_read_timeout 30s (raise only if upstream needs it)

Buffers

  • Keep moderate (16k/8×16k) as in config


6) What NOT to do on 1–2GB

  • Don’t set worker_connections 800000 (RAM cannot handle it)

  • Don’t set giant proxy buffers

  • Don’t keep keepalive very high (idle conns will eat RAM)

  • Don’t rely on swap for capacity (swap = latency + instability)


7) Monitoring commands (use during load)

Connection counts

ss -s
ss -Hnt state established '( sport = :443 or sport = :80 )' | wc -l

NGINX process limits

cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep -i "open files"

Memory pressure + OOM checks

free -m
vmstat 1 5
dmesg -T | egrep -i 'oom|killed|out of memory' | tail -n 20

Backlog / drops

netstat -s | egrep -i 'listen|overflow|drop|retransmit|SYN'

8) Quick troubleshooting

“Real IP not showing”

  • Ensure cloudflare-realip.conf is included inside http {}

  • Ensure you’re actually behind Cloudflare (orange cloud enabled)

  • Test endpoint /ip

“Still capped at ~1000 connections”

  • systemd LimitNOFILE not applied

  • Check /proc/.../limits for nginx (soft should be 100000)

“High latency under load”

  • Too many idle keepalives → keepalive_timeout too high

  • Upstream slow → check $upstream_response_time in logs

  • Swap usage → swappiness too high or RAM too low


9) Safe next steps (optional)

If attacks / abuse happen, add rate limits per IP in vhost:

  • limit_req and limit_conn

That’s all. Thank you for visiting us.

  • 0 Users Found This Useful
Was this answer helpful?

Related Articles

How to restrict direct root access in Linux

We can do it just in two steps. Step One: At first we will create new root user as follows (for...

How to extract .tar.gz files in Linux/UNIX OS

A tarball is a group of files that are bundled together using the tar command. Use the...

How to add welcome message when SSH start?

You need to change the contents of /etc/motd. Unfortunately, by default, /etc/motd is a link to...

How to change root password when SSH logged in

Run the following command: passwd Now type your new passwordOnce done, retype new passwordDone!...

How to install Pinguzo on any Linux/UNIX OS

Login to Pinguzo panel using Softaculous account or create an account of Pinguzo To add new...