Pertama kali saya deploy aplikasi Node.js ke VPS, saya langsung kena masalah klasik: aplikasi jalan di port 3000, tapi user harus ketik :3000 di URL. Enggak profesional banget dong. Solusinya? Nginx reverse proxy. Setup ini bikin aplikasi Node.js kamu bisa diakses lewat port 80/443 standar, plus dapat bonus SSL, caching, dan load balancing.
Saya udah pakai setup ini di berbagai project production, mulai dari API backend sampai full-stack Next.js app. Di tutorial ini, saya share step-by-step yang udah saya pakai berkali-kali tanpa masalah.
Sebelum masuk ke konfigurasi, penting buat ngerti kenapa kamu butuh reverse proxy di depan aplikasi Node.js:
Pastikan kamu udah punya:
Update dulu sistem kamu:
sudo apt update && sudo apt upgrade -y
Install Nginx dari repository resmi Ubuntu:
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx
Kalau muncul output active (running), berarti Nginx udah jalan. Coba buka http://IP_VPS di browser, harusnya muncul halaman welcome Nginx.
Contoh aplikasi Express sederhana di /var/www/myapp/app.js:
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({ message: 'Hello from Node.js behind Nginx!' });
});
app.listen(PORT, '127.0.0.1', () => {
console.log(`Server running on port ${PORT}`);
});
Penting: pastikan aplikasi listen di 127.0.0.1, bukan 0.0.0.0. Ini supaya aplikasi cuma bisa diakses dari localhost, enggak langsung dari internet. Biar Nginx yang handle semua request dari luar.
Supaya aplikasi tetap jalan walau VPS reboot, pakai PM2:
npm install -g pm2
cd /var/www/myapp
pm2 start app.js --name myapp
pm2 startup
pm2 save
Cek apakah aplikasi udah jalan:
curl http://127.0.0.1:3000
Harusnya muncul response JSON dari aplikasi kamu.
Sekarang bikin konfigurasi Nginx untuk domain kamu. Buat file baru:
sudo nano /etc/nginx/sites-available/myapp
Isi dengan konfigurasi ini:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Logging
access_log /var/log/nginx/myapp_access.log;
error_log /var/log/nginx/myapp_error.log;
# Proxy ke Node.js
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_read_timeout 86400;
}
# Cache file statis
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf)$ {
proxy_pass http://127.0.0.1:3000;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Ganti example.com dengan domain kamu yang sebenarnya.
Aktifkan site dan test konfigurasi:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Kalau output test is successful, berarti konfigurasi udah benar. Coba akses http://example.com di browser.
HTTPS wajib hukumnya di tahun 2026. Untungnya ada Let's Encrypt yang gratis. Pakai Certbot:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
Ikuti wizard-nya: masukkan email, setuju terms, pilih redirect HTTP ke HTTPS. Selesai!
Certbot otomatis:
Cek auto-renewal:
sudo certbot renew --dry-run
Kalau enggak ada error, SSL kamu udah siap dan bakal auto-renew sebelum expired.
Kalau aplikasi kamu pakai WebSocket (Socket.io misalnya), konfigurasi yang saya tulis di atas udah termasuk support WebSocket lewat header Upgrade dan Connection.
Tapi kalau kamu mau lebih spesifik, tambahkan location khusus:
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
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;
proxy_send_timeout 86400;
}
Setting proxy_read_timeout 86400 itu 24 jam, supaya koneksi WebSocket enggak keputus karena timeout. Ini penting banget buat aplikasi chat atau real-time notification yang user buka seharian.
Edit file /etc/nginx/nginx.conf untuk tuning performa. Config default Nginx itu konservatif banget, cocok buat development tapi kurang optimal buat production:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Buffer sizes
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# Client limits
client_max_body_size 50M;
client_body_timeout 60s;
client_header_timeout 60s;
# Keepalive
keepalive_timeout 65;
keepalive_requests 1000;
include /etc/nginx/sites-enabled/*;
}
Restart Nginx setelah edit:
sudo nginx -t && sudo systemctl restart nginx
Setting worker_processes auto bikin Nginx otomatis pakai semua CPU core yang tersedia. worker_connections 4096 artinya setiap worker bisa handle 4096 koneksi bersamaan. Kalau VPS kamu 4 core, total bisa handle 16384 koneksi sekaligus.
Tambahkan security headers di server block untuk proteksi ekstra:
# Security headers
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;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
Header-header ini cegah clickjacking, MIME sniffing, dan XSS attack. Enggak ada alasan buat enggak pasang ini. Saya pernah kena masalah karena enggak pasang X-Frame-Options - website saya bisa di-embed di iframe orang lain buat phishing.
Kalau kamu pakai API dan butuh CORS, tambahkan juga:
add_header Access-Control-Allow-Origin "https://yourfrontend.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
Biar aplikasi kamu enggak kena serangan DDoS atau brute force, pasang rate limiting:
# Di block http (nginx.conf)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# Di server block
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
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;
}
Artinya: maksimal 10 request per detik per IP, dengan burst sampai 20 request. Lebih dari itu, user dapat error 503. Ini cukup proteksi buat kebanyakan kasus.
Kamu juga bisa bikin zone terpisah buat login endpoint yang lebih ketat:
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
location /api/login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
Kalau aplikasi kamu udah rame dan butuh horizontal scaling, kamu bisa jalanin beberapa instance Node.js dan pakai Nginx sebagai load balancer:
upstream node_backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://node_backend;
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;
}
}
PM2 bisa handle ini otomatis dengan cluster mode:
pm2 start app.js --name myapp -i max
Flag -i max bikin PM2 spawn satu worker per CPU core. Kalau VPS kamu 4 core, bakal ada 4 instance. Update Nginx upstream sesuai jumlah port yang dipakai PM2.
Ada tiga strategy load balancing yang bisa dipilih:
Beberapa masalah yang sering saya temuin:
curl http://127.0.0.1:3000proxy_read_timeoutclient_max_body_sizels -la /var/www/myappKalau ada masalah, selalu cek log Nginx dulu:
# Access log
tail -f /var/log/nginx/myapp_access.log
# Error log
tail -f /var/log/nginx/myapp_error.log
Log ini bakal kasih tau persis apa yang salah. 90% masalah Nginx bisa di-diagnose cuma dari baca error log.
Nginx reverse proxy itu standar industri buat deploy aplikasi Node.js di production. Dengan setup di atas, kamu udah punya:
Semua konfigurasi di atas udah saya test di production dan works dengan baik. Tinggal sesuaikan nama domain dan port sesuai kebutuhan aplikasi kamu. Kalau ada pertanyaan atau masalah, cek error log Nginx dulu - 90% masalah biasanya keliatan di situ.
Satu tips terakhir: setelah deploy, cek konfigurasi kamu dengan tool online seperti SSL Labs (ssllabs.com/ssltest) buat memastikan SSL kamu dapat grade A. Dan jangan lupa backup konfigurasi Nginx kamu secara berkala - sekali setup, biasanya enggak diubah lagi, tapi kalau VPS crash kamu bakal butuh backup-nya.