Nginx Reverse Proxy untuk Banyak Subdomain: Setup dan Verifikasi

Konfigurasi Nginx untuk reverse proxy, static SPA, WebSocket, TLS, forwarded header, dan banyak subdomain pada satu VPS.

· · 8 min read

Nginx Reverse Proxy untuk Banyak Subdomain: Konfigurasi dan Verifikasi

Satu VPS dapat melayani beberapa domain dengan memetakan setiap hostname ke service berbeda. Nginx menerima koneksi publik di port 80/443, memilih server block berdasarkan hostname, lalu meneruskan request ke upstream lokal atau melayani file statis.

Contoh arsitektur:

msncode.dev -> 127.0.0.1:4321
api.msncode.dev -> 127.0.0.1:3000
tools.msncode.dev -> /var/www/tools/dist

Port aplikasi tidak perlu diekspos ke internet. Bind service ke loopback dan buka hanya 80/443 pada firewall.

---

DNS Dulu, Baru Nginx

Buat record A untuk IPv4 dan AAAA hanya jika VPS benar-benar memiliki konektivitas IPv6:

msncode.dev A 203.0.113.10
api.msncode.dev A 203.0.113.10
tools.msncode.dev A 203.0.113.10

Verifikasi dari luar server:

dig +short msncode.dev A
dig +short api.msncode.dev A

DNS hanya menunjuk ke IP. Pemilihan aplikasi dilakukan Nginx berdasarkan Host/SNI.

---

Reverse Proxy untuk Aplikasi

Simpan konfigurasi per situs, misalnya /etc/nginx/sites-available/msncode.dev:

server {
listen 80;
listen [::]:80;
server_name msncode.dev www.msncode.dev;

location / {
proxy_pass http://127.0.0.1:4321;
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;
proxy_set_header X-Forwarded-Proto $scheme;

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

Perhatikan slash pada proxy_pass. Tanpa URI:

location /api/ {
proxy_pass http://127.0.0.1:3000;
}

request /api/users diteruskan dengan URI tersebut. Dengan slash:

location /api/ {
proxy_pass http://127.0.0.1:3000/;
}

prefix /api/ diganti oleh /, sehingga upstream menerima /users. Perbedaan kecil ini sering menyebabkan 404 atau route ganda.

---

Static SPA Membutuhkan Fallback

Untuk hasil build React/Vue yang benar-benar berupa file statis:

server {
listen 80;
listen [::]:80;
server_name tools.msncode.dev;

root /var/www/tools/dist;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}

location ~* \.(?:css|js|png|jpg|jpeg|gif|svg|webp|woff2)$ {
try_files $uri =404;
expires 7d;
add_header Cache-Control "public";
}
}

Fallback /index.html dibutuhkan untuk client-side route seperti /calculator/json. Jangan memakai fallback ini untuk API karena error API dapat berubah menjadi HTML berstatus 200.

File dengan nama hash dapat diberi cache lebih lama dan immutable; index.html sebaiknya tidak dicache lama agar menunjuk ke asset build terbaru.

---

WebSocket Memerlukan Header Upgrade

Header Upgrade dan Connection bersifat hop-by-hop dan tidak diteruskan otomatis. Tambahkan mapping pada konteks http:

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

Lalu pada lokasi WebSocket:

location /socket/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

Jangan menambahkan header upgrade ke semua route jika hanya satu endpoint yang membutuhkannya.

---

TLS dan Redirect

Setelah DNS sudah mengarah dan port 80 dapat dijangkau, gunakan ACME client seperti Certbot sesuai dokumentasi distribusi. Konfigurasi akhir biasanya memiliki redirect HTTP:

server {
listen 80;
listen [::]:80;
server_name msncode.dev www.msncode.dev;
return 301 https://msncode.dev$request_uri;
}

Sertifikat wildcard seperti *.msncode.dev membutuhkan DNS-01 challenge dan tidak mencakup apex msncode.dev kecuali nama apex juga dimasukkan. Sertifikat terpisah per hostname sering lebih sederhana.

Jangan menyalin path sertifikat dari contoh internet. Biarkan ACME client mengelola path dan renewal, lalu uji renewal:

sudo certbot renew --dry-run

---

Trust Proxy pada Aplikasi

Nginx menambahkan X-Forwarded-For dan X-Forwarded-Proto, tetapi aplikasi harus mempercayainya hanya ketika request memang datang dari proxy yang dikenal.

Jika backend dapat diakses langsung dari internet, client dapat memalsukan header tersebut. Bind backend ke 127.0.0.1, batasi firewall, dan konfigurasi trust-proxy framework secara sempit. Jangan memakai nilai forwarded untuk keputusan keamanan sebelum trust boundary jelas.

---

Security Header Tidak Bisa Disalin Buta

Header seperti HSTS dan Content Security Policy berguna, tetapi dapat merusak situs jika diterapkan tanpa inventaris resource.

Contoh awal yang terbatas:

add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Aktifkan HSTS hanya setelah seluruh domain yang tercakup benar-benar siap HTTPS. Opsi includeSubDomains memengaruhi semua subdomain; preload memiliki konsekuensi jangka panjang. CSP sebaiknya disusun dari script, style, image, font, frame, dan endpoint koneksi yang benar-benar digunakan, lalu diuji.

---

Deploy dan Verifikasi

Aktifkan situs, uji syntax, lalu reload:

sudo ln -s /etc/nginx/sites-available/msncode.dev \
/etc/nginx/sites-enabled/msncode.dev

sudo nginx -t
sudo systemctl reload nginx

reload menerapkan konfigurasi baru secara graceful jika syntax valid. Verifikasi:

curl -I http://msncode.dev/
curl -I https://msncode.dev/
curl -I https://tools.msncode.dev/calculator/json
sudo journalctl -u nginx --since '10 minutes ago'

Untuk menguji virtual host sebelum DNS tersebar:

curl --resolve msncode.dev:443:203.0.113.10 \
https://msncode.dev/

Kesalahan yang Sering Terjadi

Default server menampilkan situs yang salah. Request dengan hostname yang tidak cocok akan masuk ke default server untuk address/port tersebut. Buat default yang disengaja dan jangan mengandalkan urutan file secara kebetulan.

Redirect loop setelah memakai CDN atau load balancer. Nginx atau aplikasi mungkin melihat koneksi internal sebagai HTTP walaupun pengguna memakai HTTPS. Tentukan trust boundary dan forwarded protocol dengan benar; jangan percaya header dari internet secara langsung.

Upstream mengembalikan 502. Periksa service sedang hidup, address/port benar, bind interface, firewall lokal, dan log error Nginx. curl http://127.0.0.1:PORT/health dari VPS membantu memisahkan masalah upstream dari konfigurasi proxy.

Upload besar gagal. Batas client_max_body_size, timeout, dan buffering harus disesuaikan dengan kebutuhan endpoint. Jangan menaikkan batas secara global tanpa proteksi autentikasi, storage, dan rate limit.

IP client salah di aplikasi. Gunakan rantai X-Forwarded-For hanya dengan daftar proxy tepercaya. Jika ada CDN di depan Nginx, ikuti mekanisme real-IP resmi penyedia dan batasi sumber koneksi ke jaringan CDN bila memungkinkan.

Backup dan Perubahan Bertahap

Sebelum mengubah situs aktif, simpan salinan konfigurasi yang sedang bekerja dan catat tujuan setiap perubahan. Terapkan satu kelompok perubahan, jalankan pemeriksaan syntax, reload, lalu lakukan smoke test dari luar VPS. Cara ini membuat sumber regresi lebih mudah ditemukan.

Jangan mengedit file hasil kelola otomatis Certbot tanpa memahami apakah perubahan akan dipertahankan saat renewal. Pisahkan konfigurasi upstream, redirect, dan parameter bersama secara masuk akal, tetapi hindari rantai include yang sulit ditelusuri.

Pantau access log, error log, status upstream, penggunaan file descriptor, dan masa berlaku sertifikat. Rotasi log perlu diuji agar disk tidak penuh. Untuk deployment dengan traffic penting, health check upstream dan strategi rollback sama pentingnya dengan syntax Nginx yang valid.

Checklist akhir:

1. DNS A/AAAA benar dan jalurnya aktif.
2. Upstream hanya mendengarkan pada interface yang diperlukan.
3. server_name unik dan default server dipahami.
4. URI proxy_pass diuji untuk path berslash dan tanpa slash.
5. SPA fallback tidak menangkap API.
6. TLS renewal diuji.
7. Backend mempercayai forwarded header secara terbatas.
8. nginx -t, response header, route dalam, dan log sudah diperiksa.

Referensi

• Nginx: HTTP Proxy Module
• Nginx: WebSocket proxying
• Nginx: Request processing
• Certbot instructions