Pertama kali saya deploy aplikasi web langsung ke port 8080 dan akses via IP:port, rasanya kok kurang profesional ya. User harus ngetik port manual, browser kadang warning soal HTTP, dan kalau mau SSL harus setup ulang di tiap aplikasi. Setelah kenal Nginx sebagai reverse proxy, semuanya berubah. Satu entry point, SSL termination di satu tempat, dan aplikasi backend tetap aman di balik Nginx.
Reverse proxy itu konsepnya sederhana: Nginx duduk di depan aplikasi kamu, nerima request dari user, terus forward ke backend yang sebenarnya. Tapi manfaatnya luar biasa banyak - dari SSL, load balancing, caching, sampai security hardening. Mari kita setup dari nol.
Bayangkan kamu punya 3 aplikasi berjalan di server yang sama:
Tanpa reverse proxy, user harus akses http://server:3000, http://server:8000, dst. Dengan Nginx sebagai reverse proxy, semua bisa diakses lewat port 80/443 dengan path atau subdomain berbeda:
https://app.example.com/ -> App A (port 3000)https://app.example.com/api/ -> App B (port 8000)https://app.example.com/admin/ -> App C (port 8080)Nginx juga bisa handle SSL termination, jadi backend kamu cukup jalan di HTTP biasa. Nginx yang urus semua hal berbau HTTPS ke client.
Kalau belum terinstall, pasang dulu Nginx:
sudo apt update
sudo apt install nginx -y
# Cek status
sudo systemctl status nginx
# Enable auto-start saat boot
sudo systemctl enable nginx
Setelah install, buka browser dan akses http://IP_SERVER_KAMU. Harusnya muncul halaman default Nginx. Kalau belum muncul, cek firewall:
sudo ufw allow 'Nginx Full'
sudo ufw status
Contoh paling simpel: proxy semua request ke aplikasi Node.js di port 3000. Buat file config baru:
sudo nano /etc/nginx/sites-available/myapp
Isi dengan konfigurasi berikut:
server {
listen 80;
server_name app.example.com;
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;
}
}
Penjelasan directive penting:
Aktifkan site dan restart Nginx:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Selalu jalankan nginx -t dulu sebelum reload. Syntax error di config bisa bikin Nginx mati total.
Ini yang sering bikin orang bingung. Kamu bisa routing berdasarkan path URL ke backend berbeda:
server {
listen 80;
server_name mydomain.com;
# Frontend (React/Vue build)
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# API backend
location /api/ {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Admin panel
location /admin/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Perhatikan trailing slash di proxy_pass. Kalau kamu tulis proxy_pass http://127.0.0.1:8000/; dengan trailing slash, maka request ke /api/users akan diteruskan ke backend sebagai /users (path /api di-strip). Tanpa trailing slash, path diteruskan apa adanya.
Sekarang HTTPS sudah wajib. Browser Chrome bakal kasih warning "Not Secure" kalau website kamu masih HTTP. Untungnya ada Let's Encrypt yang kasih SSL gratis.
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Generate SSL certificate
sudo certbot --nginx -d app.example.com
# Auto-renewal (sudah ter-setup otomatis, tapi bisa test manual)
sudo certbot renew --dry-run
Certbot otomatis modifikasi config Nginx kamu. Hasilnya kurang lebih seperti ini:
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
Perhatikan ada dua blok server. Yang pertama redirect HTTP ke HTTPS. Yang kedua handle SSL dan proxy ke backend.
Kalau aplikasi kamu sudah besar dan perlu handle traffic tinggi, kamu bisa jalankan beberapa instance backend dan Nginx akan distribusikan request:
upstream backend_cluster {
# Round-robin (default)
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
# Weighted - server pertama dapat 2x traffic
# server 127.0.0.1:3001 weight=2;
# server 127.0.0.1:3002 weight=1;
# server 127.0.0.1:3003 weight=1;
# Backup server (hanya aktif kalau semua utama down)
# server 127.0.0.1:3004 backup;
# Keepalive connection ke backend
keepalive 32;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_pass http://backend_cluster;
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;
}
}
Metode load balancing yang tersedia:
Untuk least connections, tambahkan directive:
upstream backend_cluster {
least_conn;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
Nginx bisa cache response dari backend supaya request berikutnya lebih cepat. Ini sangat efektif untuk konten yang jarang berubah:
# Di blok http (nginx.conf)
proxy_cache_path /var/cache/nginx levels=1:2
keys_zone=my_cache:10m max_size=1g
inactive=60m use_temp_path=off;
server {
listen 443 ssl;
server_name app.example.com;
# Static files - cache lama
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2)$ {
proxy_pass http://127.0.0.1:3000;
proxy_cache my_cache;
proxy_cache_valid 200 30d;
proxy_cache_valid 404 1m;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Dynamic content - cache pendek
location / {
proxy_pass http://127.0.0.1:3000;
proxy_cache my_cache;
proxy_cache_valid 200 10m;
proxy_cache_bypass $http_cache_control;
add_header X-Cache-Status $upstream_cache_status;
}
}
Header X-Cache-Status berguna banget untuk debugging. Nilainya bisa HIT (served from cache), MISS (fetch dari backend), atau BYPASS (cache di-skip).
Ini bagian yang sering di-skip tapi sangat penting. Berikut config Nginx yang lebih aman:
server {
listen 443 ssl http2;
server_name app.example.com;
# SSL config
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# 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;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Hide Nginx version
server_tokens off;
# Limit request body size (anti-DDoS)
client_max_body_size 10M;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Block access to hidden files
location ~ /\. {
deny all;
return 404;
}
}
Penjelasan security headers:
Rate limiting wajib dipasang di endpoint API supaya tidak ada yang bisa spam request. Definisikan zone-nya di blok http:
# Di nginx.conf, blok http {}
limit_req_zone $binary_remote_addr zone=api_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=login_zone:10m rate=1r/s;
Lalu terapkan di location block:
# API - max 5 request per detik
location /api/ {
limit_req zone=api_zone burst=10 nodelay;
proxy_pass http://127.0.0.1:8000/;
}
# Login - max 1 request per detik (lebih ketat)
location /auth/login {
limit_req zone=login_zone burst=3 nodelay;
proxy_pass http://127.0.0.1:8000/auth/login;
}
Parameter burst membolehkan request melebihi rate dalam waktu singkat. nodelay artinya request burst langsung diproses, tidak ditahan.
Kalau aplikasi kamu pakai WebSocket (chat, real-time dashboard, live notification), Nginx perlu config khusus:
location /ws/ {
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;
# Timeout untuk koneksi idle
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
Tanpa header Upgrade dan Connection, WebSocket handshake akan gagal. Ini error yang sering bikin orang bingung kenapa WebSocket tidak connect padahal backend sudah jalan.
Nginx punya access log dan error log yang sangat berguna untuk debugging:
# Lihat access log real-time
sudo tail -f /var/log/nginx/access.log
# Lihat error log
sudo tail -f /var/log/nginx/error.log
# Cek status koneksi aktif
sudo ss -tlnp | grep nginx
# Hitung request per menit dari access log
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f1-3 | uniq -c | tail -20
Kamu juga bisa tambahkan nginx-module-vts untuk dashboard monitoring real-time yang menampilkan request rate, bandwidth, dan response time per upstream.
Beberapa masalah yang sering muncul saat pakai Nginx reverse proxy:
curl http://127.0.0.1:PORTproxy_read_timeout 300s;client_max_body_size 50m;
location /api/ {
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://127.0.0.1:8000/;
}
proxy_set_header X-Real-IP dan pastikan backend baca dari header yang benarIni template yang sudah saya pakai di production, tinggal sesuaikan:
# /etc/nginx/sites-available/production
upstream app_backend {
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 32;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL
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;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Security
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# Gzip
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;
# Size limits
client_max_body_size 20M;
# Static files
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2|ttf)$ {
proxy_pass http://app_backend;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Application
location / {
proxy_pass http://app_backend;
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 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
# Block hidden files
location ~ /\. {
deny all;
}
}
Nginx reverse proxy memang terlihat simpel di permukaan, tapi konfigurasinya sangat fleksibel. Dari satu file config, kamu bisa handle SSL, caching, load balancing, security, rate limiting, dan WebSocket. Skill yang wajib dikuasai kalau kamu serius di web development atau DevOps.