Node.js App

Node.js App #

Node.js memiliki HTTP server built-in, tapi menjalankannya langsung di port 80/443 bukan praktik yang baik. Nginx di depan Node.js memberikan banyak keuntungan: HTTPS termination, static file serving yang efisien, gzip compression, rate limiting, dan kemampuan menjalankan multiple instances di belakang load balancer.

Konfigurasi Dasar #

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    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://localhost:3000;

        # Header standar untuk Node.js
        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;
    }
}

Dua baris proxy_http_version 1.1 dan proxy_set_header Connection "" adalah pasangan wajib untuk keepalive ke Node.js. Tanpa ini Nginx menggunakan HTTP/1.0 yang tidak mendukung keepalive — setiap request membuat koneksi TCP baru ke Node.js.


Konfigurasi Production-Ready #

Konfigurasi lengkap dengan semua optimasi yang disarankan:

upstream nodejs_app {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 1024;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Strict-Transport-Security "max-age=31536000" always;

    # File statis — Nginx layani langsung, lebih efisien dari Node.js
    location /static/ {
        alias /var/www/myapp/public/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    location /uploads/ {
        alias /var/www/myapp/uploads/;
        expires 30d;
        access_log off;
    }

    # Semua request lain ke Node.js
    location / {
        proxy_pass http://nodejs_app;
        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_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 16k;

        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Multiple Instances: Manfaatkan Semua CPU Core #

Node.js berjalan dalam satu thread. Untuk memanfaatkan server multi-core, jalankan beberapa instance Node.js di port berbeda dan load balance dengan Nginx:

upstream nodejs_cluster {
    # Satu instance per core (server 4 core = 4 instance)
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;

    keepalive 64;
}

Atau gunakan PM2 yang bisa mengelola multiple Node.js instances dan menghubungkannya lewat satu Unix socket:

upstream nodejs_pm2 {
    server unix:/var/run/myapp.sock;
    keepalive 16;
}

location / {
    proxy_pass http://nodejs_pm2;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    # ...
}

Unix socket lebih cepat dari TCP loopback karena menghindari overhead network stack.


Membaca IP Klien yang Benar di Node.js #

Setelah Nginx menambahkan X-Forwarded-For, aplikasi Node.js perlu membacanya dengan benar:

// Express.js — aktifkan trust proxy
const app = express();
app.set('trust proxy', 1);  // Trust satu proxy (Nginx)

// Sekarang req.ip akan berisi IP klien asli
app.get('/whoami', (req, res) => {
    res.json({ ip: req.ip });
});

Tanpa trust proxy, req.ip di Express selalu mengembalikan IP Nginx (127.0.0.1), bukan IP klien asli.


Deployment Zero-Downtime dengan Nginx #

Saat deploy versi baru Node.js, PM2 bisa melakukan rolling restart sementara Nginx tetap melayani request:

# PM2 akan restart instance satu per satu
# Nginx terus mengirim request ke instance yang masih running
pm2 reload myapp --update-env

# Atau dengan zero-downtime manual:
# 1. Start instance baru di port berbeda
# 2. Update konfigurasi Nginx untuk tambah instance baru
# 3. Reload Nginx
# 4. Matikan instance lama

Nginx mendeteksi instance yang sudah dimatikan melalui max_fails dan otomatis berhenti mengirim request ke sana sambil menunggu instance baru siap.


Ringkasan #

  • proxy_http_version 1.1 + proxy_set_header Connection "" adalah pasangan wajib untuk keepalive ke Node.js — jangan terlewat.
  • Layani file statis dengan Nginx langsung (bukan lewat Node.js) — jauh lebih efisien untuk CSS, JS, gambar.
  • Unix socket (server unix:/...) lebih cepat dari TCP loopback untuk komunikasi Nginx ↔ Node.js di server yang sama.
  • Di Express.js, aktifkan trust proxy agar req.ip mengembalikan IP klien asli, bukan IP Nginx.
  • Untuk memanfaatkan multi-core, jalankan satu instance Node.js per core dan load balance dengan upstream block.

← Sebelumnya: Dynamic Module   Berikutnya: PHP-FPM →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact